#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h" #include "Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h" LatteTextureReadbackInfoVk::LatteTextureReadbackInfoVk(VkDevice device, LatteTextureView* textureView) : LatteTextureReadbackInfo(textureView), m_device(device) { m_image_size = GetImageSize(textureView); } LatteTextureReadbackInfoVk::~LatteTextureReadbackInfoVk() { } uint32 LatteTextureReadbackInfoVk::GetImageSize(LatteTextureView* textureView) { const auto* baseTexture = (LatteTextureVk*)textureView->baseTexture; // handle format const auto textureFormat = baseTexture->GetFormat(); if (textureView->format == Latte::E_GX2SURFFMT::R8_G8_B8_A8_UNORM) { cemu_assert(textureFormat == VK_FORMAT_R8G8B8A8_UNORM); return baseTexture->width * baseTexture->height * 4; } else if (textureView->format == Latte::E_GX2SURFFMT::R8_UNORM ) { cemu_assert(textureFormat == VK_FORMAT_R8_UNORM); return baseTexture->width * baseTexture->height * 1; } else if (textureView->format == Latte::E_GX2SURFFMT::R8_G8_B8_A8_SRGB) { cemu_assert(textureFormat == VK_FORMAT_R8G8B8A8_SRGB); return baseTexture->width * baseTexture->height * 4; } else if (textureView->format == Latte::E_GX2SURFFMT::R32_G32_B32_A32_FLOAT) { cemu_assert(textureFormat == VK_FORMAT_R32G32B32A32_SFLOAT); return baseTexture->width * baseTexture->height * 16; } else if (textureView->format == Latte::E_GX2SURFFMT::R32_FLOAT) { cemu_assert(textureFormat == VK_FORMAT_R32_SFLOAT || textureFormat == VK_FORMAT_D32_SFLOAT); if (baseTexture->isDepth) return baseTexture->width * baseTexture->height * 4; else return baseTexture->width * baseTexture->height * 4; } else if (textureView->format == Latte::E_GX2SURFFMT::R16_UNORM) { cemu_assert(textureFormat == VK_FORMAT_R16_UNORM); if (baseTexture->isDepth) { cemu_assert_debug(false); return baseTexture->width * baseTexture->height * 2; } else { return baseTexture->width * baseTexture->height * 2; } } else if (textureView->format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_FLOAT) { cemu_assert(textureFormat == VK_FORMAT_R16G16B16A16_SFLOAT); return baseTexture->width * baseTexture->height * 8; } else if (textureView->format == Latte::E_GX2SURFFMT::R8_G8_UNORM) { cemu_assert(textureFormat == VK_FORMAT_R8G8_UNORM); return baseTexture->width * baseTexture->height * 2; } else if (textureView->format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_UNORM) { cemu_assert(textureFormat == VK_FORMAT_R16G16B16A16_UNORM); return baseTexture->width * baseTexture->height * 8; } else if (textureView->format == Latte::E_GX2SURFFMT::D24_S8_UNORM) { cemu_assert(textureFormat == VK_FORMAT_D24_UNORM_S8_UINT); // todo - if driver does not support VK_FORMAT_D24_UNORM_S8_UINT this is represented as VK_FORMAT_D32_SFLOAT_S8_UINT which is 8 bytes return baseTexture->width * baseTexture->height * 4; } else if (textureView->format == Latte::E_GX2SURFFMT::R5_G6_B5_UNORM ) { if(textureFormat == VK_FORMAT_R5G6B5_UNORM_PACK16){ return baseTexture->width * baseTexture->height * 2; } return 0; } else { cemuLog_log(LogType::Force, "Unsupported texture readback format {:04x}", (uint32)textureView->format); cemu_assert_debug(false); return 0; } } void LatteTextureReadbackInfoVk::StartTransfer() { cemu_assert(m_textureView); auto* baseTexture = (LatteTextureVk*)m_textureView->baseTexture; baseTexture->GetImageObj()->flagForCurrentCommandBuffer(); cemu_assert_debug(m_textureView->firstSlice == 0); cemu_assert_debug(m_textureView->firstMip == 0); cemu_assert_debug(m_textureView->baseTexture->dim != Latte::E_DIM::DIM_3D); VkBufferImageCopy region{}; region.bufferOffset = m_buffer_offset; region.bufferRowLength = baseTexture->width; region.bufferImageHeight = baseTexture->height; region.imageSubresource.aspectMask = baseTexture->GetImageAspect(); region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageSubresource.mipLevel = 0; region.imageOffset = {0,0,0}; region.imageExtent = {(uint32)baseTexture->width,(uint32)baseTexture->height,1}; const auto renderer = VulkanRenderer::GetInstance(); renderer->draw_endRenderPass(); renderer->barrier_image(baseTexture, region.imageSubresource, VK_IMAGE_LAYOUT_GENERAL); renderer->barrier_sequentializeTransfer(); vkCmdCopyImageToBuffer(renderer->getCurrentCommandBuffer(), baseTexture->GetImageObj()->m_image, VK_IMAGE_LAYOUT_GENERAL, m_buffer, 1, ®ion); renderer->barrier_sequentializeTransfer(); renderer->barrier_image(baseTexture, region.imageSubresource, VK_IMAGE_LAYOUT_GENERAL); // make sure transfer is finished before image is modified renderer->barrier_bufferRange(m_buffer, m_buffer_offset, m_image_size); // make sure transfer is finished before result is read m_associatedCommandBufferId = renderer->GetCurrentCommandBufferId(); m_textureView = nullptr; // to decrease latency of readbacks make sure that the current command buffer is submitted soon renderer->RequestSubmitSoon(); renderer->RequestSubmitOnIdle(); } bool LatteTextureReadbackInfoVk::IsFinished() { const auto renderer = VulkanRenderer::GetInstance(); return renderer->HasCommandBufferFinished(m_associatedCommandBufferId); } void LatteTextureReadbackInfoVk::ForceFinish() { const auto renderer = VulkanRenderer::GetInstance(); renderer->WaitCommandBufferFinished(m_associatedCommandBufferId); }