From 13c6d497a0b5fbed55dd1ff140ab15cbe564d2cc Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Wed, 24 Dec 2025 17:12:56 +0100 Subject: [PATCH] Vulkan: Refactor HandleScreenshotRequest --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 273 ++++++++---------- 1 file changed, 122 insertions(+), 151 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index a767957a..0e29acca 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -842,19 +842,24 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad auto texViewVk = (LatteTextureViewVk*)texView; auto baseImageTex = texViewVk->GetBaseImage(); - baseImageTex->GetImageObj()->flagForCurrentCommandBuffer(); - auto baseImageTexVkImage = baseImageTex->GetImageObj()->m_image; - //auto baseImageObj = baseImage->GetTextureImageView(); + auto textureVk = baseImageTex->GetImageObj(); + textureVk->flagForCurrentCommandBuffer(); - auto dumpImage = baseImageTex->GetImageObj()->m_image; - //dumpImage->flagForCurrentCommandBuffer(); + auto dumpImage = textureVk->m_image; + auto baseImage = dumpImage; int width, height; baseImageTex->GetEffectiveSize(width, height, 0); VkImage image = nullptr; - VkDeviceMemory imageMemory = nullptr;; + VkDeviceMemory imageMemory = nullptr; + + if (texViewVk->firstMip != 0) + { + cemuLog_log(LogType::Force, "Failed to capture screenshot: capturing non-zero mip is not supported"); + return; + } auto format = baseImageTex->GetFormat(); if (format != VK_FORMAT_R8G8B8A8_UNORM && format != VK_FORMAT_R8G8B8A8_SRGB && format != VK_FORMAT_R8G8B8_UNORM && format != VK_FORMAT_R8G8B8_SNORM) @@ -869,137 +874,114 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad vkGetPhysicalDeviceFormatProperties(m_physicalDevice, blitFormat, &formatProps); supportsBlit &= (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0; - // convert texture using blitting - if (supportsBlit) + if (!supportsBlit) { - VkImageCreateInfo imageInfo{}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.format = blitFormat; - imageInfo.extent = { (uint32)width, (uint32)height, 1 }; - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.arrayLayers = 1; - imageInfo.mipLevels = 1; - imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - - if (vkCreateImage(m_logicalDevice, &imageInfo, nullptr, &image) != VK_SUCCESS) - return; - - VkMemoryRequirements memRequirements; - vkGetImageMemoryRequirements(m_logicalDevice, image, &memRequirements); - - VkMemoryAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocInfo.allocationSize = memRequirements.size; - uint32 memIndex; - bool foundMemory = memoryManager->FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memIndex); - if(!foundMemory) - { - cemuLog_log(LogType::Force, "Screenshot request failed due to incompatible vulkan memory types."); - return; - } - allocInfo.memoryTypeIndex = memIndex; - - if (vkAllocateMemory(m_logicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) - { - vkDestroyImage(m_logicalDevice, image, nullptr); - return; - } - - vkBindImageMemory(m_logicalDevice, image, imageMemory, 0); - - // prepare dest image - { - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - vkCmdPipelineBarrier(getCurrentCommandBuffer(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - } - // prepare src image for blitting - { - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = baseImageTexVkImage; - barrier.subresourceRange.aspectMask = baseImageTex->GetImageAspect(); - barrier.subresourceRange.baseMipLevel = texViewVk->firstMip; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = texViewVk->firstSlice; - barrier.subresourceRange.layerCount = 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - vkCmdPipelineBarrier(getCurrentCommandBuffer(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - } - - VkOffset3D blitSize{ width, height, 1 }; - VkImageBlit imageBlitRegion{}; - imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBlitRegion.srcSubresource.layerCount = 1; - imageBlitRegion.srcOffsets[1] = blitSize; - imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBlitRegion.dstSubresource.layerCount = 1; - imageBlitRegion.dstOffsets[1] = blitSize; - - // Issue the blit command - vkCmdBlitImage(getCurrentCommandBuffer(), baseImageTexVkImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST); - - // dest image to general layout - { - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - vkCmdPipelineBarrier(getCurrentCommandBuffer(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - } - // transition image back - { - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = baseImageTexVkImage; - barrier.subresourceRange.aspectMask = baseImageTex->GetImageAspect(); - barrier.subresourceRange.baseMipLevel = texViewVk->firstMip; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = texViewVk->firstSlice; - barrier.subresourceRange.layerCount = 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - vkCmdPipelineBarrier(getCurrentCommandBuffer(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - } - - format = VK_FORMAT_R8G8B8A8_UNORM; - dumpImage = image; + cemuLog_log(LogType::Force, "Screenshot failed: Framebuffer is not in RGB8 format and blitting is unsupported"); + return; } + + // convert texture using blitting + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.format = blitFormat; + imageInfo.extent = {(uint32)width, (uint32)height, 1}; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.arrayLayers = 1; + imageInfo.mipLevels = 1; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + + if (vkCreateImage(m_logicalDevice, &imageInfo, nullptr, &image) != VK_SUCCESS) + return; + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(m_logicalDevice, image, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + uint32 memIndex; + bool foundMemory = memoryManager->FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memIndex); + if (!foundMemory) + { + vkDestroyImage(m_logicalDevice, image, nullptr); + cemuLog_log(LogType::Force, "Screenshot request failed due to incompatible vulkan memory types."); + return; + } + allocInfo.memoryTypeIndex = memIndex; + + if (vkAllocateMemory(m_logicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) + { + vkDestroyImage(m_logicalDevice, image, nullptr); + cemuLog_log(LogType::Force, "Screenshot request failed due to failed memory allocation."); + return; + } + + vkBindImageMemory(m_logicalDevice, image, imageMemory, 0); + + // prepare dst image for blitting + { + VkImageSubresourceRange range; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + // TRANSFER_READ is here only to silence validation as srcStageMask = 0 is only supported when using synchronization2 + barrier_image(image, range, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + } + // prepare src image for blitting + { + VkImageSubresourceLayers range; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.mipLevel = 0; + range.baseArrayLayer = texViewVk->firstSlice; + range.layerCount = 1; + barrier_image(baseImageTex, range, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + } + + VkOffset3D blitSize{width, height, 1}; + VkImageBlit imageBlitRegion{}; + imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlitRegion.srcSubresource.mipLevel = 0; + imageBlitRegion.srcSubresource.baseArrayLayer = texViewVk->firstSlice; + imageBlitRegion.srcSubresource.layerCount = 1; + imageBlitRegion.srcOffsets[1] = blitSize; + + imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlitRegion.dstSubresource.mipLevel = 0; + imageBlitRegion.dstSubresource.baseArrayLayer = 0; + imageBlitRegion.dstSubresource.layerCount = 1; + imageBlitRegion.dstOffsets[1] = blitSize; + + // Issue the blit command + vkCmdBlitImage(m_state.currentCommandBuffer, dumpImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST); + + // dest image to general layout + { + VkImageSubresourceRange range; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + barrier_image(image, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + } + // transition image back + { + VkImageSubresourceLayers range; + range.aspectMask = baseImageTex->GetImageAspect(); + range.mipLevel = 0; + range.baseArrayLayer = texViewVk->firstSlice; + range.layerCount = 1; + barrier_image(baseImageTex, range, VK_IMAGE_LAYOUT_GENERAL); + } + + format = VK_FORMAT_R8G8B8A8_UNORM; + dumpImage = image; } uint32 size; @@ -1043,25 +1025,14 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad memoryManager->CreateBuffer(size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, buffer, bufferMemory); vkMapMemory(m_logicalDevice, bufferMemory, 0, VK_WHOLE_SIZE, 0, &bufferPtr); + // if no blit was necessary a barrier still needs to be inserted and slice may not be zero + if (dumpImage == baseImage) { - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = dumpImage; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - vkCmdPipelineBarrier(getCurrentCommandBuffer(), VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + region.imageSubresource.baseArrayLayer = texViewVk->firstSlice; + barrier_image(baseImageTex, region.imageSubresource, VK_IMAGE_LAYOUT_GENERAL); } - vkCmdCopyImageToBuffer(getCurrentCommandBuffer(), dumpImage, VK_IMAGE_LAYOUT_GENERAL, buffer, 1, ®ion); + vkCmdCopyImageToBuffer(m_state.currentCommandBuffer, dumpImage, VK_IMAGE_LAYOUT_GENERAL, buffer, 1, ®ion); SubmitCommandBuffer(); WaitCommandBufferFinished(GetCurrentCommandBufferId());