mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-29 07:37:03 +00:00
Vulkan: Refactor HandleScreenshotRequest
This commit is contained in:
parent
aaf6d5d677
commit
13c6d497a0
1 changed files with 122 additions and 151 deletions
|
|
@ -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<TRANSFER_READ, TRANSFER_WRITE>(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<IMAGE_WRITE | TRANSFER_WRITE, SYNC_OP::TRANSFER_READ>(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<TRANSFER_WRITE, TRANSFER_READ>(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<TRANSFER_READ, TRANSFER_WRITE | IMAGE_WRITE>(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<IMAGE_WRITE | TRANSFER_WRITE, TRANSFER_READ>(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());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue