From f43d85921dc85ae586d69b21d28a7578835a55bd Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 22 Oct 2017 00:49:40 +1000 Subject: VideoBackends: Add AbstractStagingTexture class Can be used for asynchronous readback or upload of textures. --- Source/Core/VideoBackends/Vulkan/VKTexture.cpp | 222 ++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) (limited to 'Source/Core/VideoBackends/Vulkan/VKTexture.cpp') diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index cbb6e0c063..cfea34ff8e 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -291,7 +291,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* u32 upload_alignment = static_cast(g_vulkan_context->GetBufferImageGranularity()); u32 block_size = Util::GetBlockSize(m_texture->GetFormat()); u32 num_rows = Common::AlignUp(height, block_size) / block_size; - size_t source_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length); + size_t source_pitch = CalculateStrideForFormat(m_config.format, row_length); size_t upload_size = source_pitch * num_rows; std::unique_ptr temp_buffer; VkBuffer upload_buffer; @@ -356,4 +356,224 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* } } +VKStagingTexture::VKStagingTexture(StagingTextureType type, const TextureConfig& config, + std::unique_ptr buffer) + : AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer)) +{ +} + +VKStagingTexture::~VKStagingTexture() +{ + if (m_needs_flush) + VKStagingTexture::Flush(); +} + +std::unique_ptr VKStagingTexture::Create(StagingTextureType type, + const TextureConfig& config) +{ + size_t stride = config.GetStride(); + size_t buffer_size = stride * static_cast(config.height); + + STAGING_BUFFER_TYPE buffer_type; + VkImageUsageFlags buffer_usage; + if (type == StagingTextureType::Readback) + { + buffer_type = STAGING_BUFFER_TYPE_READBACK; + buffer_usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + else if (type == StagingTextureType::Upload) + { + buffer_type = STAGING_BUFFER_TYPE_UPLOAD; + buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + else + { + buffer_type = STAGING_BUFFER_TYPE_READBACK; + buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + VkBuffer buffer; + VkDeviceMemory memory; + bool coherent; + if (!StagingBuffer::AllocateBuffer(buffer_type, buffer_size, buffer_usage, &buffer, &memory, + &coherent)) + { + return nullptr; + } + + std::unique_ptr staging_buffer = + std::make_unique(buffer_type, buffer, memory, buffer_size, coherent); + std::unique_ptr staging_tex = std::unique_ptr( + new VKStagingTexture(type, config, std::move(staging_buffer))); + + // Use persistent mapping. + if (!staging_tex->m_staging_buffer->Map()) + return nullptr; + staging_tex->m_map_pointer = staging_tex->m_staging_buffer->GetMapPointer(); + staging_tex->m_map_stride = stride; + return staging_tex; +} + +void VKStagingTexture::CopyFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect) +{ + _assert_(m_type == StagingTextureType::Readback); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= src->GetConfig().width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= src->GetConfig().height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= m_config.width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= m_config.height); + + Texture2D* src_tex = static_cast(src)->GetRawTexIdentifier(); + CopyFromTexture(src_tex, src_rect, src_layer, src_level, dst_rect); +} + +void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect) +{ + if (m_needs_flush) + { + // Drop copy before reusing it. + g_command_buffer_mgr->RemoveFencePointCallback(this); + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + } + + VkImageLayout old_layout = src->GetLayout(); + src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // Issue the image->buffer copy, but delay it for now. + VkBufferImageCopy image_copy = {}; + VkImageAspectFlags aspect = + Util::IsDepthFormat(src->GetFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.bufferOffset = + static_cast(static_cast(dst_rect.top) * m_config.GetStride() + + static_cast(dst_rect.left) * m_texel_size); + image_copy.bufferRowLength = static_cast(m_config.width); + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource = {aspect, src_level, src_layer, 1}; + image_copy.imageOffset = {src_rect.left, src_rect.top, 0}; + image_copy.imageExtent = {static_cast(src_rect.GetWidth()), + static_cast(src_rect.GetHeight()), 1u}; + vkCmdCopyImageToBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), src->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_staging_buffer->GetBuffer(), 1, + &image_copy); + + // Restore old source texture layout. + src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); + + m_needs_flush = true; + g_command_buffer_mgr->AddFencePointCallback(this, + [this](VkCommandBuffer buf, VkFence fence) { + _assert_(m_needs_flush); + m_flush_fence = fence; + }, + [this](VkFence fence) { + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + g_command_buffer_mgr->RemoveFencePointCallback( + this); + }); +} + +void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + _assert_(m_type == StagingTextureType::Upload); + _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && + src_rect.GetHeight() == dst_rect.GetHeight()); + _assert_(src_rect.left >= 0 && static_cast(src_rect.right) <= m_config.width && + src_rect.top >= 0 && static_cast(src_rect.bottom) <= m_config.height); + _assert_(dst_rect.left >= 0 && static_cast(dst_rect.right) <= dst->GetConfig().width && + dst_rect.top >= 0 && static_cast(dst_rect.bottom) <= dst->GetConfig().height); + + if (m_needs_flush) + { + // Drop copy before reusing it. + g_command_buffer_mgr->RemoveFencePointCallback(this); + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + } + + // Flush caches before copying. + m_staging_buffer->FlushCPUCache(); + + Texture2D* dst_tex = static_cast(dst)->GetRawTexIdentifier(); + VkImageLayout old_layout = dst_tex->GetLayout(); + dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Issue the image->buffer copy, but delay it for now. + VkBufferImageCopy image_copy = {}; + image_copy.bufferOffset = + static_cast(static_cast(src_rect.top) * m_config.GetStride() + + static_cast(src_rect.left) * m_texel_size); + image_copy.bufferRowLength = static_cast(m_config.width); + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, 1}; + image_copy.imageOffset = {dst_rect.left, dst_rect.top, 0}; + image_copy.imageExtent = {static_cast(dst_rect.GetWidth()), + static_cast(dst_rect.GetHeight()), 1u}; + vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), + m_staging_buffer->GetBuffer(), dst_tex->GetImage(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + // Restore old source texture layout. + dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); + + m_needs_flush = true; + g_command_buffer_mgr->AddFencePointCallback(this, + [this](VkCommandBuffer buf, VkFence fence) { + _assert_(m_needs_flush); + m_flush_fence = fence; + }, + [this](VkFence fence) { + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + g_command_buffer_mgr->RemoveFencePointCallback( + this); + }); +} + +bool VKStagingTexture::Map() +{ + // Always mapped. + return true; +} + +void VKStagingTexture::Unmap() +{ + // Always mapped. +} + +void VKStagingTexture::Flush() +{ + if (!m_needs_flush) + return; + + // Either of the below two calls will cause the callback to fire. + g_command_buffer_mgr->RemoveFencePointCallback(this); + if (m_flush_fence != VK_NULL_HANDLE) + { + // WaitForFence should fire the callback. + g_command_buffer_mgr->WaitForFence(m_flush_fence); + } + else + { + // We don't have a fence, and are pending. That means the readback is in the current + // command buffer, and must execute it to populate the staging texture. + Util::ExecuteCurrentCommandsAndRestoreState(false, true); + } + m_needs_flush = false; + + // For readback textures, invalidate the CPU cache as there is new data there. + if (m_type == StagingTextureType::Readback) + m_staging_buffer->InvalidateCPUCache(); +} + } // namespace Vulkan -- cgit v1.2.3 From 56afebeb44f1ace990d9383e9b840fdc27610029 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Oct 2017 21:51:42 +1000 Subject: AbstractTexture: Seperate CopyRectangleFromTexture to two methods ScaleRectangleFromTexture, which does a draw, and CopyRectangleFromTexture, which where possible, does a bit-for-bit copy. --- Source/Core/VideoBackends/Vulkan/VKTexture.cpp | 58 +++++++++++--------------- 1 file changed, 24 insertions(+), 34 deletions(-) (limited to 'Source/Core/VideoBackends/Vulkan/VKTexture.cpp') diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index cfea34ff8e..f2db740551 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -163,10 +163,13 @@ void VKTexture::Unmap() m_staging_texture->Unmap(); } -void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) +void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level) { + Texture2D* src_texture = static_cast(src)->GetRawTexIdentifier(); + _assert_msg_(VIDEO, static_cast(src_rect.GetWidth()) <= src_texture->GetWidth() && static_cast(src_rect.GetHeight()) <= src_texture->GetHeight(), "Source rect is too large for CopyRectangleFromTexture"); @@ -176,15 +179,11 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, "Dest rect is too large for CopyRectangleFromTexture"); VkImageCopy image_copy = { - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, - src_texture->GetLayers()}, // VkImageSubresourceLayers srcSubresource - {src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource - m_config.layers}, - {dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset - {static_cast(src_rect.GetWidth()), static_cast(src_rect.GetHeight()), - 1} // VkExtent3D extent - }; + {VK_IMAGE_ASPECT_COLOR_BIT, src_level, src_layer, src_texture->GetLayers()}, + {src_rect.left, src_rect.top, 0}, + {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, m_config.layers}, + {dst_rect.left, dst_rect.top, 0}, + {static_cast(src_rect.GetWidth()), static_cast(src_rect.GetHeight()), 1}}; // Must be called outside of a render pass. StateTracker::GetInstance()->EndRenderPass(); @@ -197,12 +196,20 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle& dst_rect, vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. + src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } -void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle& src_rect) +void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle& src_rect, + const MathUtil::Rectangle& dst_rect) { + Texture2D* src_texture = static_cast(source)->GetRawTexIdentifier(); + // Can't do this within a game render pass. StateTracker::GetInstance()->EndRenderPass(); StateTracker::GetInstance()->SetPendingRebind(); @@ -235,27 +242,10 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle& dst_rect, static_cast(src_texture->GetWidth()), static_cast(src_texture->GetHeight())); draw.EndRenderPass(); -} - -void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) -{ - auto* raw_source_texture = static_cast(source)->GetRawTexIdentifier(); - CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect); -} - -void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle& srcrect, - const MathUtil::Rectangle& dstrect) -{ - if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight()) - CopyTextureRectangle(dstrect, source, srcrect); - else - ScaleTextureRectangle(dstrect, source, srcrect); // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. - source->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } -- cgit v1.2.3 From db1d9de933a914ff0c35a179d3ba1112fb0bcab1 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 19 Nov 2017 17:46:00 +1000 Subject: AbstractTexture: Drop slow map readback path --- Source/Core/VideoBackends/Vulkan/VKTexture.cpp | 50 -------------------------- 1 file changed, 50 deletions(-) (limited to 'Source/Core/VideoBackends/Vulkan/VKTexture.cpp') diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index f2db740551..d3ab16e0d9 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -113,56 +113,6 @@ void VKTexture::Bind(unsigned int stage) StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); } -std::optional VKTexture::MapFullImpl() -{ - // No support for optimization of full copy - return MapRegionImpl(0, 0, 0, m_config.width, m_config.height); -} - -std::optional VKTexture::MapRegionImpl(u32 level, u32 x, u32 y, - u32 width, u32 height) -{ - m_staging_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height, - TEXTURECACHE_TEXTURE_FORMAT); - - // Transition image to transfer source, and invalidate the current state, - // since we'll be executing the command buffer. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - StateTracker::GetInstance()->EndRenderPass(); - - // Copy to download buffer. - m_staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, x, y, width, - height, level, 0); - - // Restore original state of texture. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - // Block until the GPU has finished copying to the staging texture. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Map the staging texture so we can copy the contents out. - if (!m_staging_texture->Map()) - { - PanicAlert("Failed to map staging texture"); - return {}; - } - - return AbstractTexture::RawTextureInfo{reinterpret_cast(m_staging_texture->GetMapPointer()), - static_cast(m_staging_texture->GetRowStride()), width, - height}; -} - -void VKTexture::Unmap() -{ - if (!m_staging_texture) - return; - - m_staging_texture->Unmap(); -} - void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle& dst_rect, -- cgit v1.2.3 From 7f217a8bb24da323042081a02b65f84f9ce9761f Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 14 Nov 2017 00:21:48 +1000 Subject: Vulkan: Drop StagingTexture2D class as it is now duplicated functionality --- Source/Core/VideoBackends/Vulkan/VKTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoBackends/Vulkan/VKTexture.cpp') diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index d3ab16e0d9..59be6a0eba 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -14,7 +14,7 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/FramebufferManager.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" +#include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoBackends/Vulkan/Util.h" -- cgit v1.2.3