new UI system
This commit is contained in:
parent
d840a81448
commit
216972e73a
82 changed files with 4837 additions and 3243 deletions
|
|
@ -37,16 +37,13 @@ module;
|
|||
#include <print>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
#include <windows.h>
|
||||
#include <cassert>
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
#include "vulkan/vulkan.h"
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
#include "vulkan/vulkan_wayland.h"
|
||||
|
|
@ -54,17 +51,14 @@ module;
|
|||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
#include "vulkan/vulkan_win32.h"
|
||||
#endif
|
||||
#endif
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "../lib/stb_image_write.h"
|
||||
module Crafter.Graphics:Window_impl;
|
||||
import :Window;
|
||||
import :Transform2D;
|
||||
import :MouseElement;
|
||||
import :Device;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import :VulkanTransition;
|
||||
import :DescriptorHeapVulkan;
|
||||
import :PipelineRTVulkan;
|
||||
#endif
|
||||
import :RenderPass;
|
||||
import std;
|
||||
|
||||
using namespace Crafter;
|
||||
|
|
@ -336,52 +330,24 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||
case WM_LBUTTONDOWN: {
|
||||
window->mouseLeftHeld = true;
|
||||
window->onMouseLeftClick.Invoke();
|
||||
for(MouseElement* element : window->mouseElements) {
|
||||
if(element) {
|
||||
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
||||
element->onMouseLeftClick.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_LBUTTONUP: {
|
||||
window->mouseLeftHeld = false;
|
||||
window->onMouseLeftRelease.Invoke();
|
||||
for(MouseElement* element : window->mouseElements) {
|
||||
if(element) {
|
||||
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
||||
element->onMouseLeftRelease.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_RBUTTONDOWN: {
|
||||
window->mouseRightHeld = true;
|
||||
window->onMouseRightClick.Invoke();
|
||||
for(MouseElement* element : window->mouseElements) {
|
||||
if(element) {
|
||||
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
||||
element->onMouseRightClick.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_RBUTTONUP: {
|
||||
window->mouseRightHeld = false;
|
||||
window->onMouseRightRelease.Invoke();
|
||||
for(MouseElement* element : window->mouseElements) {
|
||||
if(element) {
|
||||
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) {
|
||||
element->onMouseRightRelease.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -405,11 +371,7 @@ Window::Window(std::uint32_t width, std::uint32_t height, const std::string_view
|
|||
SetTitle(title);
|
||||
}
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
||||
Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height(height), renderer(width, height) {
|
||||
#else
|
||||
Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height(height) {
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
Device::windows.push_back(this);
|
||||
surface = wl_compositor_create_surface(Device::compositor);
|
||||
|
|
@ -434,39 +396,7 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
|
|||
wp_viewport_set_destination(wpViewport, std::ceil(width/scale), std::ceil(height/scale));
|
||||
|
||||
wl_surface_commit(surface);
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
||||
// Create a wl_buffer, attach it to the surface and commit the surface
|
||||
int stride = width * 4;
|
||||
int size = stride * height;
|
||||
|
||||
// Allocate a shared memory file with the right size
|
||||
int fd = create_shm_file(size);
|
||||
if (fd < 0) {
|
||||
throw std::runtime_error(std::format("creating a buffer file for {}B failed", size));
|
||||
}
|
||||
|
||||
// Map the shared memory file
|
||||
renderer.buffer[0] = reinterpret_cast<Vector<std::uint8_t, 4, 4>*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
if (renderer.buffer[0] == MAP_FAILED) {
|
||||
throw std::runtime_error("mmap failed");
|
||||
}
|
||||
|
||||
wl_shm_pool *pool = wl_shm_create_pool(Device::shm, fd, size);
|
||||
buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (buffer == nullptr) {
|
||||
throw std::runtime_error("wl_buffer creation failed");
|
||||
}
|
||||
|
||||
wl_surface_attach(surface, buffer, 0, 0);
|
||||
wl_surface_commit(surface);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
// Initialize the window class
|
||||
|
|
@ -574,7 +504,6 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
|
|||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
|
||||
submitInfo.pNext = VK_NULL_HANDLE;
|
||||
#endif
|
||||
|
||||
lastMousePos = {0,0};
|
||||
mouseDelta = {0,0};
|
||||
|
|
@ -588,13 +517,11 @@ void Window::SetTitle(const std::string_view title) {
|
|||
}
|
||||
|
||||
void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) {
|
||||
new (&cursorRenderer) Rendertarget<std::uint8_t, 4, 4, 1>(sizeX, sizeY);
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
if(cursorSurface == nullptr) {
|
||||
cursorSurface = wl_compositor_create_surface(Device::compositor);
|
||||
} else {
|
||||
wl_buffer_destroy(cursorWlBuffer);
|
||||
munmap(cursorRenderer.buffer[0], cursorBufferOldSize);
|
||||
}
|
||||
|
||||
int stride = sizeX * 4;
|
||||
|
|
@ -607,11 +534,6 @@ void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) {
|
|||
throw std::runtime_error(std::format("creating a buffer file for {}B failed", size));
|
||||
}
|
||||
|
||||
cursorRenderer.buffer[0] = reinterpret_cast<Vector<std::uint8_t, 4, 4>*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
if (cursorRenderer.buffer[0] == MAP_FAILED) {
|
||||
throw std::runtime_error("mmap failed");
|
||||
}
|
||||
|
||||
wl_shm_pool *pool = wl_shm_create_pool(Device::shm, fd, size);
|
||||
cursorWlBuffer = wl_shm_pool_create_buffer(pool, 0, sizeX, sizeY, stride, WL_SHM_FORMAT_ARGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
|
@ -673,21 +595,11 @@ void Window::SetCusorImageDefault() {
|
|||
|
||||
void Window::UpdateCursorImage() {
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
cursorRenderer.Render(0);
|
||||
for(std::uint32_t i = 0; i < cursorBufferOldSize / 4; i++) {
|
||||
std::swap(cursorRenderer.buffer[0][i].b, cursorRenderer.buffer[0][i].r);
|
||||
}
|
||||
wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0);
|
||||
wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999);
|
||||
wl_surface_commit(cursorSurface);
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
cursorRenderer.Render(0);
|
||||
|
||||
// Swap R and B channels (renderer is RGBA, GDI DIB is BGRA)
|
||||
for (std::uint32_t i = 0; i < (std::uint32_t)(cursorSizeX * cursorSizeY); i++) {
|
||||
std::swap(cursorRenderer.buffer[0][i].r, cursorRenderer.buffer[0][i].b);
|
||||
}
|
||||
|
||||
// Create a mask bitmap (all zeros = fully opaque, alpha comes from color bitmap)
|
||||
HBITMAP hMask = CreateBitmap(cursorSizeX, cursorSizeY, 1, 1, nullptr);
|
||||
|
|
@ -781,15 +693,6 @@ void Window::Update() {
|
|||
}
|
||||
|
||||
void Window::Render() {
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
||||
renderer.Render(0);
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
wl_surface_attach(surface, buffer, 0, 0);
|
||||
wl_surface_commit(surface);
|
||||
wl_surface_damage(surface, 0, 0, 10000, 100000);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
// Acquire the next image from the swap chain
|
||||
Device::CheckVkResult(vkAcquireNextImageKHR(Device::device, swapChain, UINT64_MAX, semaphores.presentComplete, (VkFence)nullptr, ¤tBuffer));
|
||||
submitInfo.commandBufferCount = 1;
|
||||
|
|
@ -810,7 +713,7 @@ void Window::Render() {
|
|||
VkImageMemoryBarrier image_memory_barrier {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
|
|
@ -819,7 +722,7 @@ void Window::Render() {
|
|||
.subresourceRange = range
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||||
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||||
|
||||
onUpdate.Invoke({startTime, startTime-lastFrameBegin});
|
||||
#ifdef CRAFTER_TIMING
|
||||
|
|
@ -831,31 +734,48 @@ void Window::Render() {
|
|||
}
|
||||
#endif
|
||||
|
||||
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->pipeline);
|
||||
|
||||
VkBindHeapInfoEXT resourceHeapInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
|
||||
.heapRange = {
|
||||
.address = descriptorHeap->resourceHeap[currentBuffer].address,
|
||||
.size = static_cast<std::uint32_t>(descriptorHeap->resourceHeap[currentBuffer].size)
|
||||
},
|
||||
.reservedRangeOffset = (descriptorHeap->resourceHeap[currentBuffer].size - Device::descriptorHeapProperties.minResourceHeapReservedRange) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1),
|
||||
.reservedRangeSize = Device::descriptorHeapProperties.minResourceHeapReservedRange
|
||||
};
|
||||
Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo);
|
||||
if (descriptorHeap) {
|
||||
VkBindHeapInfoEXT resourceHeapInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
|
||||
.heapRange = {
|
||||
.address = descriptorHeap->resourceHeap[currentBuffer].address,
|
||||
.size = static_cast<std::uint32_t>(descriptorHeap->resourceHeap[currentBuffer].size)
|
||||
},
|
||||
.reservedRangeOffset = (descriptorHeap->resourceHeap[currentBuffer].size - Device::descriptorHeapProperties.minResourceHeapReservedRange) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1),
|
||||
.reservedRangeSize = Device::descriptorHeapProperties.minResourceHeapReservedRange
|
||||
};
|
||||
Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo);
|
||||
|
||||
VkBindHeapInfoEXT samplerHeapInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
|
||||
.heapRange = {
|
||||
.address = descriptorHeap->samplerHeap[currentBuffer].address,
|
||||
.size = static_cast<std::uint32_t>(descriptorHeap->samplerHeap[currentBuffer].size)
|
||||
},
|
||||
.reservedRangeOffset = descriptorHeap->samplerHeap[currentBuffer].size - Device::descriptorHeapProperties.minSamplerHeapReservedRange,
|
||||
.reservedRangeSize = Device::descriptorHeapProperties.minSamplerHeapReservedRange
|
||||
};
|
||||
Device::vkCmdBindSamplerHeapEXT(drawCmdBuffers[currentBuffer], &samplerHeapInfo);
|
||||
VkBindHeapInfoEXT samplerHeapInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
|
||||
.heapRange = {
|
||||
.address = descriptorHeap->samplerHeap[currentBuffer].address,
|
||||
.size = static_cast<std::uint32_t>(descriptorHeap->samplerHeap[currentBuffer].size)
|
||||
},
|
||||
.reservedRangeOffset = descriptorHeap->samplerHeap[currentBuffer].size - Device::descriptorHeapProperties.minSamplerHeapReservedRange,
|
||||
.reservedRangeSize = Device::descriptorHeapProperties.minSamplerHeapReservedRange
|
||||
};
|
||||
Device::vkCmdBindSamplerHeapEXT(drawCmdBuffers[currentBuffer], &samplerHeapInfo);
|
||||
}
|
||||
|
||||
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &pipeline->raygenRegion, &pipeline->missRegion, &pipeline->hitRegion, &pipeline->callableRegion, width, height, 1);
|
||||
// Note: vkCmdClearColorImage is unavailable here — the swapchain is
|
||||
// created with VK_IMAGE_USAGE_STORAGE_BIT only (no TRANSFER_DST_BIT).
|
||||
// Passes that need a background should write one explicitly (UIScene
|
||||
// exposes a `background()` setter for this purpose).
|
||||
(void)clearColor;
|
||||
|
||||
for (std::size_t i = 0; i < passes.size(); ++i) {
|
||||
passes[i]->Record(drawCmdBuffers[currentBuffer], currentBuffer, *this);
|
||||
|
||||
if (i + 1 < passes.size()) {
|
||||
VkMemoryBarrier mb {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
|
||||
};
|
||||
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &mb, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier image_memory_barrier2 {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
|
|
@ -869,7 +789,7 @@ void Window::Render() {
|
|||
.subresourceRange = range
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier2);
|
||||
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier2);
|
||||
|
||||
Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer]));
|
||||
|
||||
|
|
@ -894,7 +814,6 @@ void Window::Render() {
|
|||
Device::CheckVkResult(result);
|
||||
}
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CRAFTER_TIMING
|
||||
|
|
@ -935,7 +854,6 @@ void Window::LogTiming() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
void Window::CreateSwapchain()
|
||||
{
|
||||
// Store the current swap chain handle so we can use it later on to ease up recreation
|
||||
|
|
@ -1009,7 +927,7 @@ void Window::CreateSwapchain()
|
|||
swapchainCI.imageFormat = colorFormat;
|
||||
swapchainCI.imageColorSpace = colorSpace;
|
||||
swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
|
||||
swapchainCI.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
swapchainCI.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
|
||||
swapchainCI.imageArrayLayers = 1;
|
||||
swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
|
@ -1120,8 +1038,6 @@ void Window::EndCmd(VkCommandBuffer cmd) {
|
|||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
void Window::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t time) {
|
||||
wl_callback_destroy(cb);
|
||||
|
|
@ -1167,4 +1083,135 @@ void Window::xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_
|
|||
window->scale = scale / 120.0f;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void Window::SaveFrame(const std::filesystem::path& path) {
|
||||
// Staging buffer big enough for one RGBA frame.
|
||||
VkDeviceSize bufSize = static_cast<VkDeviceSize>(width) * height * 4;
|
||||
|
||||
VkBufferCreateInfo bci{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = bufSize,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
};
|
||||
VkBuffer stagingBuf = VK_NULL_HANDLE;
|
||||
Device::CheckVkResult(vkCreateBuffer(Device::device, &bci, nullptr, &stagingBuf));
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetBufferMemoryRequirements(Device::device, stagingBuf, &memReqs);
|
||||
VkMemoryAllocateInfo mai{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT),
|
||||
};
|
||||
VkDeviceMemory stagingMem = VK_NULL_HANDLE;
|
||||
Device::CheckVkResult(vkAllocateMemory(Device::device, &mai, nullptr, &stagingMem));
|
||||
Device::CheckVkResult(vkBindBufferMemory(Device::device, stagingBuf, stagingMem, 0));
|
||||
|
||||
// One-shot command buffer so we don't trash the per-frame ones.
|
||||
VkCommandBufferAllocateInfo cba{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = Device::commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
VkCommandBuffer cmd = VK_NULL_HANDLE;
|
||||
Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &cba, &cmd));
|
||||
|
||||
VkCommandBufferBeginInfo cbi{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
Device::CheckVkResult(vkBeginCommandBuffer(cmd, &cbi));
|
||||
|
||||
VkImageSubresourceRange range{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
// Render() leaves the image in PRESENT_SRC_KHR.
|
||||
VkImageMemoryBarrier toSrc{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = images[currentBuffer],
|
||||
.subresourceRange = range,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmd,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &toSrc);
|
||||
|
||||
VkBufferImageCopy region{
|
||||
.bufferOffset = 0,
|
||||
.bufferRowLength = 0,
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||||
.imageOffset = { 0, 0, 0 },
|
||||
.imageExtent = { width, height, 1 },
|
||||
};
|
||||
vkCmdCopyImageToBuffer(cmd, images[currentBuffer],
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingBuf, 1, ®ion);
|
||||
|
||||
VkImageMemoryBarrier back{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.dstAccessMask = 0,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = images[currentBuffer],
|
||||
.subresourceRange = range,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmd,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &back);
|
||||
|
||||
Device::CheckVkResult(vkEndCommandBuffer(cmd));
|
||||
|
||||
VkSubmitInfo si{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd,
|
||||
};
|
||||
Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &si, VK_NULL_HANDLE));
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
|
||||
// Read back, swizzle BGRA → RGBA if needed, write PNG.
|
||||
void* mapped = nullptr;
|
||||
Device::CheckVkResult(vkMapMemory(Device::device, stagingMem, 0, VK_WHOLE_SIZE, 0, &mapped));
|
||||
const std::uint8_t* src = static_cast<const std::uint8_t*>(mapped);
|
||||
|
||||
std::vector<std::uint8_t> rgba(static_cast<std::size_t>(width) * height * 4);
|
||||
bool bgr = (colorFormat == VK_FORMAT_B8G8R8A8_UNORM);
|
||||
for (std::uint32_t i = 0; i < width * height; ++i) {
|
||||
if (bgr) {
|
||||
rgba[i*4+0] = src[i*4+2];
|
||||
rgba[i*4+1] = src[i*4+1];
|
||||
rgba[i*4+2] = src[i*4+0];
|
||||
rgba[i*4+3] = src[i*4+3];
|
||||
} else {
|
||||
rgba[i*4+0] = src[i*4+0];
|
||||
rgba[i*4+1] = src[i*4+1];
|
||||
rgba[i*4+2] = src[i*4+2];
|
||||
rgba[i*4+3] = src[i*4+3];
|
||||
}
|
||||
}
|
||||
vkUnmapMemory(Device::device, stagingMem);
|
||||
|
||||
stbi_write_png(path.string().c_str(), static_cast<int>(width), static_cast<int>(height),
|
||||
4, rgba.data(), static_cast<int>(width) * 4);
|
||||
|
||||
vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd);
|
||||
vkDestroyBuffer(Device::device, stagingBuf, nullptr);
|
||||
vkFreeMemory(Device::device, stagingMem, nullptr);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue