diff options
| author | Stenzek <stenzek@users.noreply.github.com> | 2017-12-01 14:24:06 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-01 14:24:06 +1000 |
| commit | cd68b3606cea8a7041751bda482170533bae3220 (patch) | |
| tree | 4edef80bda42a82a50ff9f9e86d64aac38c8aff3 /Source/Core/VideoBackends/Vulkan | |
| parent | 0773a48c6c990c488e70ff7c1f46713593440ddc (diff) | |
| parent | 7f217a8bb24da323042081a02b65f84f9ce9761f (diff) | |
Merge pull request #6193 from stenzek/readbacks
Abstract Staging Textures - VideoCommon interface for texture readbacks/uploads
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan')
17 files changed, 377 insertions, 466 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index f3d602afb6..25c12fb12c 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -11,7 +11,6 @@ set(SRCS ShaderCompiler.cpp StateTracker.cpp StagingBuffer.cpp - StagingTexture2D.cpp StreamBuffer.cpp SwapChain.cpp Texture2D.cpp diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index 51706fc33f..5d70d38584 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -371,6 +371,12 @@ void CommandBufferManager::OnCommandBufferExecuted(size_t index) FrameResources& resources = m_frame_resources[index]; // Fire fence tracking callbacks. + for (auto iter = m_fence_point_callbacks.begin(); iter != m_fence_point_callbacks.end();) + { + auto backup_iter = iter++; + backup_iter->second.second(resources.fence); + } + for (const auto& iter : m_fence_point_callbacks) iter.second.second(resources.fence); diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 78689b9638..94f773fb94 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -16,7 +16,6 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" @@ -698,7 +697,7 @@ u32 FramebufferManager::PeekEFBColor(u32 x, u32 y) return 0; u32 value; - m_color_readback_texture->ReadTexel(x, y, &value, sizeof(value)); + m_color_readback_texture->ReadTexel(x, y, &value); return value; } @@ -711,7 +710,6 @@ bool FramebufferManager::PopulateColorReadbackTexture() // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}}; Texture2D* src_texture = m_efb_color_texture.get(); - VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; if (GetEFBSamples() > 1) src_texture = ResolveEFBColorTexture(src_region); @@ -750,9 +748,9 @@ bool FramebufferManager::PopulateColorReadbackTexture() // Copy from EFB or copy texture to staging texture. src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_color_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH, - EFB_HEIGHT, 0, 0); + static_cast<VKStagingTexture*>(m_color_readback_texture.get()) + ->CopyFromTexture(src_texture, m_color_readback_texture->GetConfig().GetRect(), 0, 0, + m_color_readback_texture->GetConfig().GetRect()); // Restore original layout if we used the EFB as a source. if (src_texture == m_efb_color_texture.get()) @@ -762,12 +760,7 @@ bool FramebufferManager::PopulateColorReadbackTexture() } // Wait until the copy is complete. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Map to host memory. - if (!m_color_readback_texture->IsMapped() && !m_color_readback_texture->Map()) - return false; - + m_color_readback_texture->Flush(); m_color_readback_texture_valid = true; return true; } @@ -778,7 +771,7 @@ float FramebufferManager::PeekEFBDepth(u32 x, u32 y) return 0.0f; float value; - m_depth_readback_texture->ReadTexel(x, y, &value, sizeof(value)); + m_depth_readback_texture->ReadTexel(x, y, &value); return value; } @@ -791,12 +784,10 @@ bool FramebufferManager::PopulateDepthReadbackTexture() // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}}; Texture2D* src_texture = m_efb_depth_texture.get(); - VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_DEPTH_BIT; if (GetEFBSamples() > 1) { // EFB depth resolves are written out as color textures src_texture = ResolveEFBDepthTexture(src_region); - src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; } if (GetEFBWidth() != EFB_WIDTH || GetEFBHeight() != EFB_HEIGHT) { @@ -828,15 +819,14 @@ bool FramebufferManager::PopulateDepthReadbackTexture() // Use this as a source texture now. src_texture = m_depth_copy_texture.get(); - src_aspect = VK_IMAGE_ASPECT_COLOR_BIT; } // Copy from EFB or copy texture to staging texture. src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_depth_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH, - EFB_HEIGHT, 0, 0); + static_cast<VKStagingTexture*>(m_depth_readback_texture.get()) + ->CopyFromTexture(src_texture, m_depth_readback_texture->GetConfig().GetRect(), 0, 0, + m_depth_readback_texture->GetConfig().GetRect()); // Restore original layout if we used the EFB as a source. if (src_texture == m_efb_depth_texture.get()) @@ -846,12 +836,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture() } // Wait until the copy is complete. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Map to host memory. - if (!m_depth_readback_texture->IsMapped() && !m_depth_readback_texture->Map()) - return false; - + m_depth_readback_texture->Flush(); m_depth_readback_texture_valid = true; return true; } @@ -1011,32 +996,27 @@ bool FramebufferManager::CreateReadbackTextures() VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - m_color_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, - EFB_HEIGHT, EFB_COLOR_TEXTURE_FORMAT); - if (!m_color_copy_texture || !m_color_readback_texture) - { - ERROR_LOG(VIDEO, "Failed to create EFB color readback texture"); - return false; - } - m_depth_copy_texture = Texture2D::Create(EFB_WIDTH, EFB_HEIGHT, 1, 1, EFB_DEPTH_AS_COLOR_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, - EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT); - if (!m_depth_copy_texture || !m_depth_readback_texture) + if (!m_color_copy_texture || !m_depth_copy_texture) { - ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture"); + ERROR_LOG(VIDEO, "Failed to create EFB copy textures"); return false; } - // With Vulkan, we can leave these textures mapped and use invalidate/flush calls instead. - if (!m_color_readback_texture->Map() || !m_depth_readback_texture->Map()) + TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, AbstractTextureFormat::RGBA8, + false); + m_color_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config); + m_depth_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config); + if (!m_color_readback_texture || !m_depth_readback_texture) { - ERROR_LOG(VIDEO, "Failed to map EFB readback textures"); + ERROR_LOG(VIDEO, "Failed to create EFB readback textures"); return false; } @@ -1113,7 +1093,7 @@ void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color) // Update the peek cache if it's valid, since we know the color of the pixel now. if (m_color_readback_texture_valid) - m_color_readback_texture->WriteTexel(x, y, &color, sizeof(color)); + m_color_readback_texture->WriteTexel(x, y, &color); } void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth) @@ -1126,7 +1106,7 @@ void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth) // Update the peek cache if it's valid, since we know the color of the pixel now. if (m_depth_readback_texture_valid) - m_depth_readback_texture->WriteTexel(x, y, &depth, sizeof(depth)); + m_depth_readback_texture->WriteTexel(x, y, &depth); } void FramebufferManager::CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x, diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h index 0c1a160728..e765abc907 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h @@ -13,9 +13,10 @@ #include "VideoCommon/FramebufferManagerBase.h" #include "VideoCommon/RenderState.h" +class AbstractStagingTexture; + namespace Vulkan { -class StagingTexture2D; class StateTracker; class StreamBuffer; class Texture2D; @@ -138,8 +139,8 @@ private: VkFramebuffer m_depth_copy_framebuffer = VK_NULL_HANDLE; // CPU-side EFB readback texture - std::unique_ptr<StagingTexture2D> m_color_readback_texture; - std::unique_ptr<StagingTexture2D> m_depth_readback_texture; + std::unique_ptr<AbstractStagingTexture> m_color_readback_texture; + std::unique_ptr<AbstractStagingTexture> m_depth_readback_texture; bool m_color_readback_texture_valid = false; bool m_depth_readback_texture_valid = false; diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 084e7a2880..742a525b98 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -159,6 +159,17 @@ void Renderer::DestroySemaphores() } } +std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config) +{ + return VKTexture::Create(config); +} + +std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return VKStagingTexture::Create(type, config); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { u32 backbuffer_width = m_swap_chain->GetWidth(); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index e8b7952e7d..b3f8168347 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -32,6 +32,10 @@ public: static Renderer* GetInstance(); + std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override; + std::unique_ptr<AbstractStagingTexture> + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; + SwapChain* GetSwapChain() const { return m_swap_chain.get(); } BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); } bool Initialize(); diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h index 65cfb1c5c7..2ecb21cb22 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h @@ -59,11 +59,11 @@ public: static std::unique_ptr<StagingBuffer> Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage); -protected: // Allocates the resources needed to create a staging buffer. static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent); +protected: STAGING_BUFFER_TYPE m_type; VkBuffer m_buffer; VkDeviceMemory m_memory; diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp deleted file mode 100644 index ee726fc8f2..0000000000 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include <algorithm> -#include <cstring> - -#include "Common/Assert.h" - -#include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" -#include "VideoBackends/Vulkan/Util.h" -#include "VideoBackends/Vulkan/VulkanContext.h" - -namespace Vulkan -{ -StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, - VkDeviceSize size, bool coherent, u32 width, u32 height, - VkFormat format, u32 stride) - : StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height), - m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride) -{ -} - -StagingTexture2D::~StagingTexture2D() -{ -} - -void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const -{ - _assert_(data_size >= m_texel_size); - - VkDeviceSize offset = y * m_row_stride + x * m_texel_size; - VkDeviceSize map_offset = offset - m_map_offset; - _assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size)); - - const char* ptr = m_map_pointer + map_offset; - memcpy(data, ptr, data_size); -} - -void StagingTexture2D::WriteTexel(u32 x, u32 y, const void* data, size_t data_size) -{ - _assert_(data_size >= m_texel_size); - - VkDeviceSize offset = y * m_row_stride + x * m_texel_size; - VkDeviceSize map_offset = offset - m_map_offset; - _assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size)); - - char* ptr = m_map_pointer + map_offset; - memcpy(ptr, data, data_size); -} - -void StagingTexture2D::ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, - u32 data_stride) const -{ - const char* src_ptr = GetRowPointer(y); - - // Optimal path: same dimensions, same stride. - _assert_((x + width) <= m_width && (y + height) <= m_height); - if (x == 0 && width == m_width && m_row_stride == data_stride) - { - memcpy(data, src_ptr, m_row_stride * height); - return; - } - - u32 copy_size = std::min(width * m_texel_size, data_stride); - char* dst_ptr = reinterpret_cast<char*>(data); - for (u32 row = 0; row < height; row++) - { - memcpy(dst_ptr, src_ptr + (x * m_texel_size), copy_size); - src_ptr += m_row_stride; - dst_ptr += data_stride; - } -} - -void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, - u32 data_stride) -{ - char* dst_ptr = GetRowPointer(y); - - // Optimal path: same dimensions, same stride. - _assert_((x + width) <= m_width && (y + height) <= m_height); - if (x == 0 && width == m_width && m_row_stride == data_stride) - { - memcpy(dst_ptr, data, m_row_stride * height); - return; - } - - u32 copy_size = std::min(width * m_texel_size, data_stride); - const char* src_ptr = reinterpret_cast<const char*>(data); - for (u32 row = 0; row < height; row++) - { - memcpy(dst_ptr + (x * m_texel_size), src_ptr, copy_size); - dst_ptr += m_row_stride; - src_ptr += data_stride; - } -} - -void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Issue the image->buffer copy. - VkBufferImageCopy image_copy = { - y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset - m_width, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource - {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset - {width, height, 1} // VkExtent3D imageExtent - }; - vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, - &image_copy); - - // Flush CPU and GPU caches if not coherent mapping. - VkDeviceSize buffer_flush_offset = y * m_row_stride; - VkDeviceSize buffer_flush_size = height * m_row_stride; - FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - buffer_flush_offset, buffer_flush_size); - InvalidateCPUCache(buffer_flush_offset, buffer_flush_size); -} - -void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Flush CPU and GPU caches if not coherent mapping. - VkDeviceSize buffer_flush_offset = y * m_row_stride; - VkDeviceSize buffer_flush_size = height * m_row_stride; - FlushCPUCache(buffer_flush_offset, buffer_flush_size); - InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - buffer_flush_offset, buffer_flush_size); - - // Issue the buffer->image copy. - VkBufferImageCopy image_copy = { - y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset - m_width, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource - {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset - {width, height, 1} // VkExtent3D imageExtent - }; - vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &image_copy); -} - -std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width, - u32 height, VkFormat format) -{ - // Assume tight packing. - u32 stride = Util::GetTexelSize(format) * width; - u32 size = stride * height; - VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - VkBuffer buffer; - VkDeviceMemory memory; - bool coherent; - if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent)) - return nullptr; - - return std::make_unique<StagingTexture2D>(type, buffer, memory, size, coherent, width, height, - format, stride); -} -} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h deleted file mode 100644 index 74dbb8cf07..0000000000 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include <cstddef> -#include <memory> - -#include "Common/CommonTypes.h" -#include "VideoBackends/Vulkan/Constants.h" -#include "VideoBackends/Vulkan/StagingBuffer.h" - -namespace Vulkan -{ -class StagingTexture2D final : public StagingBuffer -{ -public: - StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, - VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format, - u32 stride); - ~StagingTexture2D(); - - u32 GetWidth() const { return m_width; } - u32 GetHeight() const { return m_height; } - VkFormat GetFormat() const { return m_format; } - u32 GetRowStride() const { return m_row_stride; } - u32 GetTexelSize() const { return m_texel_size; } - // Requires Map() to be called first. - const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; } - char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; } - void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const; - void WriteTexel(u32 x, u32 y, const void* data, size_t data_size); - void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const; - void WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride); - - // Assumes that image is in TRANSFER_SRC layout. - // Results are not ready until command_buffer has been executed. - void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); - - // Assumes that image is in TRANSFER_DST layout. - // Buffer is not safe for re-use until after command_buffer has been executed. - void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); - - // Creates the optimal format of image copy. - static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format); - -protected: - u32 m_width; - u32 m_height; - VkFormat m_format; - u32 m_texel_size; - u32 m_row_stride; -}; -} diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 1d8221f646..5a396ee4b2 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -178,11 +178,6 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u } } -std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config) -{ - return VKTexture::Create(config); -} - bool TextureCache::CreateRenderPasses() { static constexpr VkAttachmentDescription update_attachment = { diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h index 9bfa2da04e..12bf088ad1 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.h +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h @@ -31,8 +31,6 @@ public: bool CompileShaders() override; void DeleteShaders() override; - std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override; - void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette, TLUTFormat format) override; diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index b8ac91d06b..72995c7a4b 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -19,7 +19,6 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" @@ -67,9 +66,6 @@ TextureConverter::~TextureConverter() if (m_encoding_render_pass != VK_NULL_HANDLE) vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr); - if (m_encoding_render_framebuffer != VK_NULL_HANDLE) - vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr); - for (auto& it : m_encoding_shaders) vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second, nullptr); @@ -111,12 +107,6 @@ bool TextureConverter::Initialize() return false; } - if (!CreateEncodingDownloadTexture()) - { - PanicAlert("Failed to create download texture"); - return false; - } - if (!CreateDecodingTexture()) { PanicAlert("Failed to create decoding texture"); @@ -245,8 +235,10 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p // Can't do our own draw within a render pass. StateTracker::GetInstance()->EndRenderPass(); - m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + static_cast<VKTexture*>(m_encoding_render_texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), @@ -276,23 +268,15 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p render_height); VkRect2D render_region = {{0, 0}, {render_width, render_height}}; - draw.BeginRenderPass(m_encoding_render_framebuffer, render_region); + draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(), + render_region); draw.DrawWithoutVertexBuffer(4); draw.EndRenderPass(); - // Transition the image before copying - m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_encoding_download_texture->CopyFromImage( - g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(), - VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0); - - // Block until the GPU has finished copying to the staging texture. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Copy from staging texture to the final destination, adjusting pitch if necessary. - m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, - memory_stride); + MathUtil::Rectangle<int> copy_rect(0, 0, render_width, render_height); + m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, + copy_rect); + m_encoding_readback_texture->ReadTexels(copy_rect, dest_ptr, memory_stride); } void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride, @@ -304,8 +288,9 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u // Borrow framebuffer from EFB2RAM encoder. VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_encoding_render_texture->TransitionToLayout(command_buffer, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + static_cast<VKTexture*>(m_encoding_render_texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Use fragment shader to convert RGBA to YUYV. // Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in @@ -317,7 +302,8 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u m_encoding_render_pass, g_shader_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, m_rgb_to_yuyv_shader); VkRect2D region = {{0, 0}, {output_width, dst_height}}; - draw.BeginRenderPass(m_encoding_render_framebuffer, region); + draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(), + region); draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), @@ -325,18 +311,11 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u static_cast<int>(src_texture->GetHeight())); draw.EndRenderPass(); - // Render pass transitions to TRANSFER_SRC. - m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - // Copy from encoding texture to download buffer. - m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(), - VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width, - dst_height, 0, 0); - Util::ExecuteCurrentCommandsAndRestoreState(false, true); - - // Finally, copy to guest memory. This may have a different stride. - m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride); + MathUtil::Rectangle<int> copy_rect(0, 0, output_width, dst_height); + m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, + copy_rect); + m_encoding_readback_texture->ReadTexels(copy_rect, dst_ptr, dst_stride); } void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr, @@ -734,10 +713,10 @@ VkShaderModule TextureConverter::GetEncodingShader(const EFBCopyParams& params) bool TextureConverter::CreateEncodingRenderPass() { VkAttachmentDescription attachments[] = { - {0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; + {0, Util::GetVkFormatForHostTextureFormat(ENCODING_TEXTURE_FORMAT), VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; VkAttachmentReference color_attachment_references[] = { {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; @@ -769,43 +748,14 @@ bool TextureConverter::CreateEncodingRenderPass() bool TextureConverter::CreateEncodingTexture() { - m_encoding_render_texture = Texture2D::Create( - ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT, - VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - if (!m_encoding_render_texture) - return false; + TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, + ENCODING_TEXTURE_FORMAT, true); - VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()}; - VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - m_encoding_render_pass, - static_cast<u32>(ArraySize(framebuffer_attachments)), - framebuffer_attachments, - m_encoding_render_texture->GetWidth(), - m_encoding_render_texture->GetHeight(), - m_encoding_render_texture->GetLayers()}; - - VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &m_encoding_render_framebuffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); - return false; - } - - return true; -} - -bool TextureConverter::CreateEncodingDownloadTexture() -{ - m_encoding_download_texture = - StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH, - ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT); + m_encoding_render_texture = g_renderer->CreateTexture(config); + m_encoding_readback_texture = + g_renderer->CreateStagingTexture(StagingTextureType::Readback, config); - return m_encoding_download_texture && m_encoding_download_texture->Map(); + return m_encoding_render_texture && m_encoding_readback_texture; } bool TextureConverter::CreateDecodingTexture() diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.h b/Source/Core/VideoBackends/Vulkan/TextureConverter.h index 1a62590019..3adb26c5b7 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.h +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.h @@ -16,6 +16,9 @@ #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/VideoCommon.h" +class AbstractTexture; +class AbstractStagingTexture; + namespace Vulkan { class StagingTexture2D; @@ -58,7 +61,7 @@ public: private: static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4; static const u32 ENCODING_TEXTURE_HEIGHT = 1024; - static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM; + static const AbstractTextureFormat ENCODING_TEXTURE_FORMAT = AbstractTextureFormat::BGRA8; static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3; // Maximum size of a texture based on BP registers. @@ -75,8 +78,6 @@ private: bool CreateEncodingRenderPass(); bool CreateEncodingTexture(); - bool CreateEncodingDownloadTexture(); - bool CreateDecodingTexture(); bool CompileYUYVConversionShaders(); @@ -106,10 +107,9 @@ private: // Texture encoding - RGBA8->GX format in memory std::map<EFBCopyParams, VkShaderModule> m_encoding_shaders; + std::unique_ptr<AbstractTexture> m_encoding_render_texture; + std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture; VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE; - std::unique_ptr<Texture2D> m_encoding_render_texture; - VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE; - std::unique_ptr<StagingTexture2D> m_encoding_download_texture; // Texture decoding - GX format in memory->RGBA8 struct TextureDecodingPipeline diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 221fa97cda..e74435e6d6 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -107,7 +107,13 @@ VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format) return VK_FORMAT_BC7_UNORM_BLOCK; case AbstractTextureFormat::RGBA8: + return VK_FORMAT_R8G8B8A8_UNORM; + + case AbstractTextureFormat::BGRA8: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + PanicAlert("Unhandled texture format."); return VK_FORMAT_R8G8B8A8_UNORM; } } diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index cbb6e0c063..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" @@ -113,60 +113,13 @@ void VKTexture::Bind(unsigned int stage) StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); } -std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapFullImpl() +void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle<int>& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle<int>& dst_rect, + u32 dst_layer, u32 dst_level) { - // No support for optimization of full copy - return MapRegionImpl(0, 0, 0, m_config.width, m_config.height); -} - -std::optional<AbstractTexture::RawTextureInfo> 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); + Texture2D* src_texture = static_cast<const VKTexture*>(src)->GetRawTexIdentifier(); - // 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<u8*>(m_staging_texture->GetMapPointer()), - static_cast<u32>(m_staging_texture->GetRowStride()), width, - height}; -} - -void VKTexture::Unmap() -{ - if (!m_staging_texture) - return; - - m_staging_texture->Unmap(); -} - -void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle<int>& src_rect) -{ _assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() && static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(), "Source rect is too large for CopyRectangleFromTexture"); @@ -176,15 +129,11 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& 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<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(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<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()), 1}}; // Must be called outside of a render pass. StateTracker::GetInstance()->EndRenderPass(); @@ -197,12 +146,20 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& 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<int>& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle<int>& src_rect) +void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle<int>& src_rect, + const MathUtil::Rectangle<int>& dst_rect) { + Texture2D* src_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier(); + // Can't do this within a game render pass. StateTracker::GetInstance()->EndRenderPass(); StateTracker::GetInstance()->SetPendingRebind(); @@ -235,27 +192,10 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, static_cast<int>(src_texture->GetWidth()), static_cast<int>(src_texture->GetHeight())); draw.EndRenderPass(); -} - -void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle<int>& srcrect, - const MathUtil::Rectangle<int>& dstrect) -{ - auto* raw_source_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier(); - CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect); -} - -void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect, - const MathUtil::Rectangle<int>& 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); } @@ -291,7 +231,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* u32 upload_alignment = static_cast<u32>(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<StagingBuffer> temp_buffer; VkBuffer upload_buffer; @@ -356,4 +296,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<StagingBuffer> buffer) + : AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer)) +{ +} + +VKStagingTexture::~VKStagingTexture() +{ + if (m_needs_flush) + VKStagingTexture::Flush(); +} + +std::unique_ptr<VKStagingTexture> VKStagingTexture::Create(StagingTextureType type, + const TextureConfig& config) +{ + size_t stride = config.GetStride(); + size_t buffer_size = stride * static_cast<size_t>(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<StagingBuffer> staging_buffer = + std::make_unique<StagingBuffer>(buffer_type, buffer, memory, buffer_size, coherent); + std::unique_ptr<VKStagingTexture> staging_tex = std::unique_ptr<VKStagingTexture>( + 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<int>& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle<int>& 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<u32>(src_rect.right) <= src->GetConfig().width && + src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height); + _assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width && + dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height); + + Texture2D* src_tex = static_cast<const VKTexture*>(src)->GetRawTexIdentifier(); + CopyFromTexture(src_tex, src_rect, src_layer, src_level, dst_rect); +} + +void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle<int>& 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<VkDeviceSize>(static_cast<size_t>(dst_rect.top) * m_config.GetStride() + + static_cast<size_t>(dst_rect.left) * m_texel_size); + image_copy.bufferRowLength = static_cast<u32>(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<u32>(src_rect.GetWidth()), + static_cast<u32>(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<int>& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle<int>& 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<u32>(src_rect.right) <= m_config.width && + src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height); + _assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width && + dst_rect.top >= 0 && static_cast<u32>(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<const VKTexture*>(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<VkDeviceSize>(static_cast<size_t>(src_rect.top) * m_config.GetStride() + + static_cast<size_t>(src_rect.left) * m_texel_size); + image_copy.bufferRowLength = static_cast<u32>(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<u32>(dst_rect.GetWidth()), + static_cast<u32>(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 diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index 61ffae242c..426f29973d 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -7,10 +7,12 @@ #include <memory> #include <vulkan/vulkan.h> +#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" namespace Vulkan { +class StagingBuffer; class Texture2D; class VKTexture final : public AbstractTexture @@ -20,13 +22,16 @@ public: ~VKTexture(); void Bind(unsigned int stage) override; - void Unmap() override; - void CopyRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle<int>& srcrect, - const MathUtil::Rectangle<int>& dstrect) override; - void CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect, - const MathUtil::Rectangle<int>& dstrect); + void CopyRectangleFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle<int>& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle<int>& dst_rect, + u32 dst_layer, u32 dst_level) override; + void ScaleRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle<int>& src_rect, + const MathUtil::Rectangle<int>& dst_rect); + void ScaleRectangleFromTexture(Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect, + const MathUtil::Rectangle<int>& dst_rect); void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; @@ -39,21 +44,41 @@ private: VKTexture(const TextureConfig& tex_config, std::unique_ptr<Texture2D> texture, VkFramebuffer framebuffer); - // Copies the contents of a texture using vkCmdCopyImage - void CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture, - const MathUtil::Rectangle<int>& src_rect); + std::unique_ptr<Texture2D> m_texture; + VkFramebuffer m_framebuffer; +}; - // Copies (and optionally scales) the contents of a texture using a framgent shader. - void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture, - const MathUtil::Rectangle<int>& src_rect); +class VKStagingTexture final : public AbstractStagingTexture +{ +public: + VKStagingTexture() = delete; + ~VKStagingTexture(); - std::optional<RawTextureInfo> MapFullImpl() override; - std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width, - u32 height) override; + void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle<int>& dst_rect) override; + void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst, + const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer, + u32 dst_level) override; - std::unique_ptr<Texture2D> m_texture; - std::unique_ptr<StagingTexture2D> m_staging_texture; - VkFramebuffer m_framebuffer; + bool Map() override; + void Unmap() override; + void Flush() override; + + // This overload is provided for compatibility as we dropped StagingTexture2D. + // For now, FramebufferManager relies on them. But we can drop it once we move that to common. + void CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer, + u32 src_level, const MathUtil::Rectangle<int>& dst_rect); + + static std::unique_ptr<VKStagingTexture> Create(StagingTextureType type, + const TextureConfig& config); + +private: + VKStagingTexture(StagingTextureType type, const TextureConfig& config, + std::unique_ptr<StagingBuffer> buffer); + + std::unique_ptr<StagingBuffer> m_staging_buffer; + VkFence m_flush_fence = VK_NULL_HANDLE; }; } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj index 3492f7b0f2..1a24ff053b 100644 --- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj +++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj @@ -46,7 +46,6 @@ <ClCompile Include="PerfQuery.cpp" /> <ClCompile Include="RasterFont.cpp" /> <ClCompile Include="StagingBuffer.cpp" /> - <ClCompile Include="StagingTexture2D.cpp" /> <ClCompile Include="Util.cpp" /> <ClCompile Include="VertexFormat.cpp" /> <ClCompile Include="ObjectCache.cpp" /> @@ -72,7 +71,6 @@ <ClInclude Include="TextureConverter.h" /> <ClInclude Include="RasterFont.h" /> <ClInclude Include="StagingBuffer.h" /> - <ClInclude Include="StagingTexture2D.h" /> <ClInclude Include="Util.h" /> <ClInclude Include="VertexFormat.h" /> <ClInclude Include="PerfQuery.h" /> |
