Update Sources/viewport_server.cpp

This commit is contained in:
2026-04-15 08:59:27 +00:00
parent e6f4de6820
commit 54997fd259

View File

@ -13,6 +13,20 @@
#include <math.h>
#ifdef KORE_WINDOWS
#include <malloc.h>
#endif
#ifdef KORE_WINDOWS
#include <Windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#endif
#ifdef KORE_DIRECT3D11
#include <d3d11.h>
#include <dxgi.h>
@ -47,17 +61,27 @@ struct dx_context {
// declare with C linkage to match Kinc C definition
extern "C" struct dx_context dx_ctx;
#endif
#endif // KORE_DIRECT3D11
#ifdef KORE_WINDOWS
#include <Windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#endif
#ifdef KORE_DIRECT3D12
#include <d3d12.h>
#include <dxgi1_4.h>
#include <kinc/backend/graphics5/d3d12mini.h>
// D3D12 uses Graphics5 layer - access device and command queue
extern "C" ID3D12Device *device;
extern "C" ID3D12CommandQueue *commandQueue;
extern "C" ID3D12GraphicsCommandList *commandList;
#endif // KORE_DIRECT3D12
#ifdef KORE_OPENGL
#include <kinc/backend/graphics4/ogl.h>
#endif // KORE_OPENGL
#ifdef KORE_VULKAN
#include <vulkan/vulkan.h>
#include <kinc/backend/graphics5/vulkan.h>
#endif // KORE_VULKAN
// global viewport server state
static ViewportServerState g_viewport_state = {0};
@ -275,17 +299,28 @@ bool viewport_server_init(const char* shmem_name, int width, int height) {
return true;
}
#ifdef KORE_WINDOWS
#ifdef KORE_DIRECT3D11
static ID3D11Texture2D* g_stagingTexture = NULL;
static int g_stagingWidth = 0, g_stagingHeight = 0;
#endif
#ifdef KORE_DIRECT3D12
static ID3D12Resource* g_d3d12ReadbackBuffer = NULL;
static size_t g_d3d12ReadbackSize = 0;
#endif
#ifdef KORE_VULKAN
static VkBuffer g_vkReadbackBuffer = VK_NULL_HANDLE;
static VkDeviceMemory g_vkReadbackMemory = VK_NULL_HANDLE;
static size_t g_vkReadbackSize = 0;
#endif
void viewport_server_shutdown(void) {
if (!g_viewport_state.initialized) {
return;
}
#ifdef KORE_WINDOWS
#ifdef KORE_DIRECT3D11
if (g_stagingTexture) {
g_stagingTexture->Release();
g_stagingTexture = NULL;
@ -293,6 +328,26 @@ void viewport_server_shutdown(void) {
g_stagingHeight = 0;
}
#endif
#ifdef KORE_DIRECT3D12
if (g_d3d12ReadbackBuffer) {
g_d3d12ReadbackBuffer->Release();
g_d3d12ReadbackBuffer = NULL;
g_d3d12ReadbackSize = 0;
}
#endif
#ifdef KORE_VULKAN
if (g_vkReadbackBuffer != VK_NULL_HANDLE) {
vkDestroyBuffer(vk_ctx.device, g_vkReadbackBuffer, NULL);
g_vkReadbackBuffer = VK_NULL_HANDLE;
}
if (g_vkReadbackMemory != VK_NULL_HANDLE) {
vkFreeMemory(vk_ctx.device, g_vkReadbackMemory, NULL);
g_vkReadbackMemory = VK_NULL_HANDLE;
}
g_vkReadbackSize = 0;
#endif
if (g_viewport_state.render_target) {
kinc_g4_render_target_destroy((kinc_g4_render_target_t*)g_viewport_state.render_target);
@ -346,23 +401,219 @@ void viewport_server_end_frame(void) {
uint8_t* pixels = g_viewport_state.pixel_buffer;
uint8_t* pixel_dest = (uint8_t*)g_viewport_state.shmem_ptr + VIEWPORT_HEADER_SIZE;
#ifdef KORE_WINDOWS
// Direct3D11 - read from backbuffer BEFORE swap_buffers
ID3D11DeviceContext* context = dx_ctx.context;
ID3D11Device* device = dx_ctx.device;
struct dx_window* window = &dx_ctx.windows[0];
ID3D11Texture2D* backBuffer = window->backBuffer;
if (backBuffer && context && device) {
D3D11_TEXTURE2D_DESC bbDesc;
backBuffer->GetDesc(&bbDesc);
int bbWidth = (int)bbDesc.Width;
int bbHeight = (int)bbDesc.Height;
// ====================================================================
// Backend-specific framebuffer readback
// ====================================================================
#if defined(KORE_DIRECT3D11)
// --- Direct3D 11: copy backbuffer to staging texture, then map ---
{
ID3D11DeviceContext* ctx = dx_ctx.context;
ID3D11Device* dev = dx_ctx.device;
struct dx_window* win = &dx_ctx.windows[0];
ID3D11Texture2D* backBuffer = win->backBuffer;
if (backBuffer && ctx && dev) {
D3D11_TEXTURE2D_DESC bbDesc;
backBuffer->GetDesc(&bbDesc);
int bbWidth = (int)bbDesc.Width;
int bbHeight = (int)bbDesc.Height;
// capture dimensions are the actual size of whats renderedso we clamp to max shared memory size
int captureWidth = bbWidth;
int captureHeight = bbHeight;
int captureWidth = bbWidth;
int captureHeight = bbHeight;
if (captureWidth > VIEWPORT_MAX_WIDTH) captureWidth = VIEWPORT_MAX_WIDTH;
if (captureHeight > VIEWPORT_MAX_HEIGHT) captureHeight = VIEWPORT_MAX_HEIGHT;
header->width = captureWidth;
header->height = captureHeight;
pixel_size = (size_t)captureWidth * captureHeight * 4;
// same dimensions between source and destination
if (!g_stagingTexture || g_stagingWidth != bbWidth || g_stagingHeight != bbHeight) {
if (g_stagingTexture) g_stagingTexture->Release();
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = bbWidth;
desc.Height = bbHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = bbDesc.Format;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
HRESULT hr = dev->CreateTexture2D(&desc, NULL, &g_stagingTexture);
if (FAILED(hr)) {
kinc_log(KINC_LOG_LEVEL_ERROR, "D3D11: Failed to create staging texture: 0x%08X", hr);
g_stagingTexture = NULL;
g_stagingWidth = 0;
g_stagingHeight = 0;
g_viewport_state.frame_count++;
return;
}
g_stagingWidth = bbWidth;
g_stagingHeight = bbHeight;
}
ctx->CopyResource(g_stagingTexture, backBuffer);
// only read the portion we need
D3D11_MAPPED_SUBRESOURCE mapped;
if (SUCCEEDED(ctx->Map(g_stagingTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
for (int y = 0; y < captureHeight; y++) {
memcpy(pixels + y * captureWidth * 4,
(uint8_t*)mapped.pData + y * mapped.RowPitch,
captureWidth * 4);
}
ctx->Unmap(g_stagingTexture, 0);
// BGRA to RGBA conversion
for (size_t i = 0; i < pixel_size; i += 4) {
uint8_t t = pixels[i]; pixels[i] = pixels[i+2]; pixels[i+2] = t;
}
memcpy(pixel_dest, pixels, pixel_size);
}
}
}
#elif defined(KORE_DIRECT3D12)
{
int captureWidth = g_viewport_state.width;
int captureHeight = g_viewport_state.height;
if (captureWidth > VIEWPORT_MAX_WIDTH) captureWidth = VIEWPORT_MAX_WIDTH;
if (captureHeight > VIEWPORT_MAX_HEIGHT) captureHeight = VIEWPORT_MAX_HEIGHT;
header->width = captureWidth;
header->height = captureHeight;
// aligned to D3D12_TEXTURE_DATA_PITCH_ALIGNMENT (256 bytes)
UINT rowPitch = (captureWidth * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1);
size_t readbackSize = (size_t)rowPitch * captureHeight;
pixel_size = (size_t)captureWidth * captureHeight * 4;
if (!g_d3d12ReadbackBuffer || g_d3d12ReadbackSize < readbackSize) {
if (g_d3d12ReadbackBuffer) g_d3d12ReadbackBuffer->Release();
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
D3D12_RESOURCE_DESC bufDesc = {};
bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
bufDesc.Width = readbackSize;
bufDesc.Height = 1;
bufDesc.DepthOrArraySize = 1;
bufDesc.MipLevels = 1;
bufDesc.SampleDesc.Count = 1;
bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
HRESULT hr = device->CreateCommittedResource(
&heapProps, D3D12_HEAP_FLAG_NONE, &bufDesc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL,
IID_PPV_ARGS(&g_d3d12ReadbackBuffer));
if (FAILED(hr)) {
kinc_log(KINC_LOG_LEVEL_ERROR, "D3D12: Failed to create readback buffer: 0x%08X", hr);
g_d3d12ReadbackBuffer = NULL;
g_d3d12ReadbackSize = 0;
g_viewport_state.frame_count++;
return;
}
g_d3d12ReadbackSize = readbackSize;
}
ID3D12CommandAllocator* copyAllocator = NULL;
ID3D12GraphicsCommandList* copyCmd = NULL;
HRESULT hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&copyAllocator));
if (SUCCEEDED(hr)) {
hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, copyAllocator, NULL, IID_PPV_ARGS(&copyCmd));
}
if (SUCCEEDED(hr) && copyCmd) {
extern struct dx_window dx_windows[];
struct dx_window* win = &dx_windows[0];
ID3D12Resource* backBuffer = NULL;
if (win->swapChain) {
IDXGISwapChain3* swapChain3 = NULL;
if (SUCCEEDED(((IDXGISwapChain*)win->swapChain)->QueryInterface(IID_PPV_ARGS(&swapChain3)))) {
UINT backBufferIndex = swapChain3->GetCurrentBackBufferIndex();
swapChain3->GetBuffer(backBufferIndex, IID_PPV_ARGS(&backBuffer));
swapChain3->Release();
}
}
if (backBuffer) {
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition.pResource = backBuffer;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
copyCmd->ResourceBarrier(1, &barrier);
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
srcLoc.pResource = backBuffer;
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
srcLoc.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
dstLoc.pResource = g_d3d12ReadbackBuffer;
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dstLoc.PlacedFootprint.Offset = 0;
dstLoc.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
dstLoc.PlacedFootprint.Footprint.Width = captureWidth;
dstLoc.PlacedFootprint.Footprint.Height = captureHeight;
dstLoc.PlacedFootprint.Footprint.Depth = 1;
dstLoc.PlacedFootprint.Footprint.RowPitch = rowPitch;
copyCmd->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, NULL);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
copyCmd->ResourceBarrier(1, &barrier);
copyCmd->Close();
ID3D12CommandList* lists[] = { copyCmd };
commandQueue->ExecuteCommandLists(1, lists);
ID3D12Fence* fence = NULL;
HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (SUCCEEDED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))) {
commandQueue->Signal(fence, 1);
fence->SetEventOnCompletion(1, event);
WaitForSingleObject(event, 1000);
fence->Release();
}
CloseHandle(event);
void* mappedData = NULL;
D3D12_RANGE readRange = { 0, readbackSize };
if (SUCCEEDED(g_d3d12ReadbackBuffer->Map(0, &readRange, &mappedData))) {
for (int y = 0; y < captureHeight; y++) {
memcpy(pixels + y * captureWidth * 4,
(uint8_t*)mappedData + y * rowPitch,
captureWidth * 4);
}
D3D12_RANGE writeRange = { 0, 0 };
g_d3d12ReadbackBuffer->Unmap(0, &writeRange);
memcpy(pixel_dest, pixels, pixel_size);
}
backBuffer->Release();
}
}
if (copyCmd) copyCmd->Release();
if (copyAllocator) copyAllocator->Release();
}
#elif defined(KORE_OPENGL)
{
int captureWidth = g_viewport_state.width;
int captureHeight = g_viewport_state.height;
if (captureWidth > VIEWPORT_MAX_WIDTH) captureWidth = VIEWPORT_MAX_WIDTH;
if (captureHeight > VIEWPORT_MAX_HEIGHT) captureHeight = VIEWPORT_MAX_HEIGHT;
@ -370,53 +621,187 @@ void viewport_server_end_frame(void) {
header->height = captureHeight;
pixel_size = (size_t)captureWidth * captureHeight * 4;
// same dimensions between source and destination
if (!g_stagingTexture || g_stagingWidth != bbWidth || g_stagingHeight != bbHeight) {
if (g_stagingTexture) g_stagingTexture->Release();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glReadPixels(0, 0, captureWidth, captureHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
int stride = captureWidth * 4;
uint8_t* row_temp = (uint8_t*)alloca(stride);
for (int y = 0; y < captureHeight / 2; y++) {
uint8_t* top = pixels + y * stride;
uint8_t* bot = pixels + (captureHeight - 1 - y) * stride;
memcpy(row_temp, top, stride);
memcpy(top, bot, stride);
memcpy(bot, row_temp, stride);
}
memcpy(pixel_dest, pixels, pixel_size);
}
#elif defined(KORE_VULKAN)
{
int captureWidth = g_viewport_state.width;
int captureHeight = g_viewport_state.height;
if (captureWidth > VIEWPORT_MAX_WIDTH) captureWidth = VIEWPORT_MAX_WIDTH;
if (captureHeight > VIEWPORT_MAX_HEIGHT) captureHeight = VIEWPORT_MAX_HEIGHT;
header->width = captureWidth;
header->height = captureHeight;
pixel_size = (size_t)captureWidth * captureHeight * 4;
if (g_vkReadbackBuffer == VK_NULL_HANDLE || g_vkReadbackSize < pixel_size) {
if (g_vkReadbackBuffer != VK_NULL_HANDLE) {
vkDestroyBuffer(vk_ctx.device, g_vkReadbackBuffer, NULL);
vkFreeMemory(vk_ctx.device, g_vkReadbackMemory, NULL);
}
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = bbWidth;
desc.Height = bbHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = bbDesc.Format;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
VkBufferCreateInfo bufInfo = {};
bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufInfo.size = pixel_size;
bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
HRESULT hr = device->CreateTexture2D(&desc, NULL, &g_stagingTexture);
if (FAILED(hr)) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to create staging texture: 0x%08X", hr);
g_stagingTexture = NULL;
g_stagingWidth = 0;
g_stagingHeight = 0;
if (vkCreateBuffer(vk_ctx.device, &bufInfo, NULL, &g_vkReadbackBuffer) != VK_SUCCESS) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Vulkan: Failed to create readback buffer");
g_vkReadbackBuffer = VK_NULL_HANDLE;
g_vkReadbackSize = 0;
g_viewport_state.frame_count++;
return;
}
g_stagingWidth = bbWidth;
g_stagingHeight = bbHeight;
VkMemoryRequirements memReqs;
vkGetBufferMemoryRequirements(vk_ctx.device, g_vkReadbackBuffer, &memReqs);
uint32_t memTypeIndex = UINT32_MAX;
for (uint32_t i = 0; i < vk_ctx.memory_properties.memoryTypeCount; i++) {
if ((memReqs.memoryTypeBits & (1 << i)) &&
(vk_ctx.memory_properties.memoryTypes[i].propertyFlags &
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
memTypeIndex = i;
break;
}
}
if (memTypeIndex == UINT32_MAX) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Vulkan: No suitable memory type for readback");
vkDestroyBuffer(vk_ctx.device, g_vkReadbackBuffer, NULL);
g_vkReadbackBuffer = VK_NULL_HANDLE;
g_vkReadbackSize = 0;
g_viewport_state.frame_count++;
return;
}
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memReqs.size;
allocInfo.memoryTypeIndex = memTypeIndex;
if (vkAllocateMemory(vk_ctx.device, &allocInfo, NULL, &g_vkReadbackMemory) != VK_SUCCESS) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Vulkan: Failed to allocate readback memory");
vkDestroyBuffer(vk_ctx.device, g_vkReadbackBuffer, NULL);
g_vkReadbackBuffer = VK_NULL_HANDLE;
g_vkReadbackSize = 0;
g_viewport_state.frame_count++;
return;
}
vkBindBufferMemory(vk_ctx.device, g_vkReadbackBuffer, g_vkReadbackMemory, 0);
g_vkReadbackSize = pixel_size;
}
context->CopyResource(g_stagingTexture, backBuffer);
// only read the portion we need
D3D11_MAPPED_SUBRESOURCE mapped;
if (SUCCEEDED(context->Map(g_stagingTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
for (int y = 0; y < captureHeight; y++) {
memcpy(pixels + y * captureWidth * 4,
(uint8_t*)mapped.pData + y * mapped.RowPitch,
captureWidth * 4);
}
context->Unmap(g_stagingTexture, 0);
struct vk_window* win = &vk_ctx.windows[0];
if (win->images && win->current_image < win->image_count) {
VkImage swapImage = win->images[win->current_image];
// BGRA to RGBA conversion
for (size_t i = 0; i < pixel_size; i += 4) {
uint8_t t = pixels[i]; pixels[i] = pixels[i+2]; pixels[i+2] = t;
VkCommandBufferAllocateInfo cmdAllocInfo = {};
cmdAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdAllocInfo.commandPool = vk_ctx.cmd_pool;
cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmdAllocInfo.commandBufferCount = 1;
VkCommandBuffer cmdBuf;
if (vkAllocateCommandBuffers(vk_ctx.device, &cmdAllocInfo, &cmdBuf) != VK_SUCCESS) {
g_viewport_state.frame_count++;
return;
}
memcpy(pixel_dest, pixels, pixel_size);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuf, &beginInfo);
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.image = swapImage;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(cmdBuf,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
VkBufferImageCopy region = {};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {(uint32_t)captureWidth, (uint32_t)captureHeight, 1};
vkCmdCopyImageToBuffer(cmdBuf, swapImage,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
g_vkReadbackBuffer, 1, &region);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
vkCmdPipelineBarrier(cmdBuf,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
vkEndCommandBuffer(cmdBuf);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmdBuf;
vkQueueSubmit(vk_ctx.queue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(vk_ctx.queue);
vkFreeCommandBuffers(vk_ctx.device, vk_ctx.cmd_pool, 1, &cmdBuf);
void* mappedData = NULL;
if (vkMapMemory(vk_ctx.device, g_vkReadbackMemory, 0, pixel_size, 0, &mappedData) == VK_SUCCESS) {
memcpy(pixels, mappedData, pixel_size);
vkUnmapMemory(vk_ctx.device, g_vkReadbackMemory);
if (win->format.format == VK_FORMAT_B8G8R8A8_UNORM ||
win->format.format == VK_FORMAT_B8G8R8A8_SRGB) {
for (size_t i = 0; i < pixel_size; i += 4) {
uint8_t t = pixels[i]; pixels[i] = pixels[i+2]; pixels[i+2] = t;
}
}
memcpy(pixel_dest, pixels, pixel_size);
}
}
}
#else
// other platforms use render target
if (g_viewport_state.render_target) {