mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-10 01:24:41 -06:00
Requires both VK_EXT_attachment_feedback_loop and VK_EXT_attachment_feedback_loop_dynamic_state to be provided by the driver. Otherwise it will fall back to the old code. Main benefit is for improving performance as it makes barriers cheaper and doesn't force us to unnecessarily interrupt renderpasses
156 lines
5.6 KiB
C++
156 lines
5.6 KiB
C++
#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<VulkanRenderer::ANY_TRANSFER | VulkanRenderer::IMAGE_WRITE, VulkanRenderer::TRANSFER_READ>(baseTexture, region.imageSubresource, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
|
|
renderer->barrier_sequentializeTransfer();
|
|
|
|
vkCmdCopyImageToBuffer(renderer->getCurrentCommandBuffer(), baseTexture->GetImageObj()->m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, ®ion);
|
|
|
|
renderer->barrier_sequentializeTransfer();
|
|
|
|
renderer->barrier_image<VulkanRenderer::TRANSFER_READ, VulkanRenderer::ANY_TRANSFER | VulkanRenderer::IMAGE_WRITE>(baseTexture, region.imageSubresource, baseTexture->GetDefaultLayout()); // make sure transfer is finished before image is modified
|
|
renderer->barrier_bufferRange<VulkanRenderer::TRANSFER_WRITE, VulkanRenderer::HOST_READ>(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);
|
|
}
|
|
|