diff options
| author | shuffle2 <godisgovernment@gmail.com> | 2017-06-18 12:57:05 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-06-18 12:57:05 -0700 |
| commit | e63c33783049753784a24675bfa0792e54684fbd (patch) | |
| tree | 3d984535025f4cb97489c9665220ac4237e7761c /Source/Core/VideoBackends/Vulkan/TextureCache.cpp | |
| parent | 09457d3a4245ec9d4f2fdac3d1d0873a3354b858 (diff) | |
| parent | e4896d39bd927b0b8e109e0334fa15cdb8e004f3 (diff) | |
Merge pull request #5305 from iwubcode/abstract_texture
Abstract Texture
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/TextureCache.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/TextureCache.cpp | 530 |
1 files changed, 110 insertions, 420 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index 737a05c280..30d251d343 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -9,7 +9,6 @@ #include <string> #include <vector> -#include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonFuncs.h" #include "Common/Logging/Log.h" @@ -19,15 +18,16 @@ #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/Renderer.h" -#include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoBackends/Vulkan/TextureConverter.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/ImageWrite.h" +#include "VideoCommon/TextureConfig.h" namespace Vulkan { @@ -42,6 +42,21 @@ TextureCache::~TextureCache() TextureCache::DeleteShaders(); } +VkShaderModule TextureCache::GetCopyShader() const +{ + return m_copy_shader; +} + +VkRenderPass TextureCache::GetTextureCopyRenderPass() const +{ + return m_render_pass; +} + +StreamBuffer* TextureCache::GetTextureUploadBuffer() const +{ + return m_texture_upload_buffer.get(); +} + TextureCache* TextureCache::GetInstance() { return static_cast<TextureCache*>(g_texture_cache.get()); @@ -80,19 +95,20 @@ bool TextureCache::Initialize() return true; } -void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* base_unconverted, - void* palette, TlutFormat format) +void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette, + TlutFormat format) { - TCacheEntry* entry = static_cast<TCacheEntry*>(base_entry); - TCacheEntry* unconverted = static_cast<TCacheEntry*>(base_unconverted); - - m_texture_converter->ConvertTexture(entry, unconverted, m_render_pass, palette, format); + m_texture_converter->ConvertTexture(destination, source, m_render_pass, palette, format); // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. - unconverted->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - entry->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast<VKTexture*>(source->texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast<VKTexture*>(destination->texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, @@ -136,30 +152,12 @@ void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_widt src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout); } -void TextureCache::CopyRectangleFromTexture(TCacheEntry* dst_texture, - const MathUtil::Rectangle<int>& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle<int>& src_rect) -{ - // Fast path when not scaling the image. - if (src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()) - CopyTextureRectangle(dst_texture, dst_rect, src_texture, src_rect); - else - ScaleTextureRectangle(dst_texture, dst_rect, src_texture, src_rect); - - // 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); - dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - bool TextureCache::SupportsGPUTextureDecode(TextureFormat format, TlutFormat palette_format) { return m_texture_converter->SupportsTextureDecoding(format, palette_format); } -void TextureCache::DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, +void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) @@ -167,144 +165,22 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, con // Group compute shader dispatches together in the init command buffer. That way we don't have to // pay a penalty for switching from graphics->compute, or end/restart our render pass. VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer(); - m_texture_converter->DecodeTexture(command_buffer, static_cast<TCacheEntry*>(entry), dst_level, - data, data_size, format, width, height, aligned_width, - aligned_height, row_stride, palette, palette_format); + m_texture_converter->DecodeTexture(command_buffer, entry, dst_level, data, data_size, format, + width, height, aligned_width, aligned_height, row_stride, + palette, palette_format); // Last mip level? Ensure the texture is ready for use. - if (dst_level == (entry->config.levels - 1)) + if (dst_level == (entry->GetNumLevels() - 1)) { - static_cast<TCacheEntry*>(entry)->GetTexture()->TransitionToLayout( - command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast<VKTexture*>(entry->texture.get()) + ->GetRawTexIdentifier() + ->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } } -void TextureCache::CopyTextureRectangle(TCacheEntry* dst_texture, - 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"); - - _assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= dst_texture->config.width && - static_cast<u32>(dst_rect.GetHeight()) <= dst_texture->config.height, - "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 - dst_texture->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 - }; - - // Must be called outside of a render pass. - StateTracker::GetInstance()->EndRenderPass(); - - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_texture->GetTexture()->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); -} - -void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture, - const MathUtil::Rectangle<int>& dst_rect, - Texture2D* src_texture, - const MathUtil::Rectangle<int>& src_rect) -{ - // Can't do this within a game render pass. - StateTracker::GetInstance()->EndRenderPass(); - StateTracker::GetInstance()->SetPendingRebind(); - - // Can't render to a non-rendertarget (no framebuffer). - _assert_msg_(VIDEO, dst_texture->config.rendertarget, - "Destination texture for partial copy is not a rendertarget"); - - // Render pass expects dst_texture to be in COLOR_ATTACHMENT_OPTIMAL state. - // src_texture should already be in SHADER_READ_ONLY state, but transition in case (XFB). - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - dst_texture->GetTexture()->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_STANDARD), m_render_pass, - g_object_cache->GetPassthroughVertexShader(), - g_object_cache->GetPassthroughGeometryShader(), m_copy_shader); - - VkRect2D region = { - {dst_rect.left, dst_rect.top}, - {static_cast<u32>(dst_rect.GetWidth()), static_cast<u32>(dst_rect.GetHeight())}}; - draw.BeginRenderPass(dst_texture->GetFramebuffer(), region); - draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); - draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(), - src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), - static_cast<int>(src_texture->GetWidth()), - static_cast<int>(src_texture->GetHeight())); - draw.EndRenderPass(); -} - -TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config) +std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config) { - // Determine image usage, we need to flag as an attachment if it can be used as a rendertarget. - VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT; - if (config.rendertarget) - usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - // Allocate texture object - VkFormat vk_format = Util::GetVkFormatForHostTextureFormat(config.format); - std::unique_ptr<Texture2D> texture = Texture2D::Create( - config.width, config.height, config.levels, config.layers, vk_format, VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); - - if (!texture) - return nullptr; - - // If this is a render target (for efb copies), allocate a framebuffer - VkFramebuffer framebuffer = VK_NULL_HANDLE; - if (config.rendertarget) - { - VkImageView framebuffer_attachments[] = {texture->GetView()}; - VkFramebufferCreateInfo framebuffer_info = { - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - m_render_pass, - static_cast<u32>(ArraySize(framebuffer_attachments)), - framebuffer_attachments, - texture->GetWidth(), - texture->GetHeight(), - texture->GetLayers()}; - - VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &framebuffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); - return nullptr; - } - - // Clear render targets before use to prevent reading uninitialized memory. - VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}}; - VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, config.levels, 0, - config.layers}; - texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), - texture->GetLayout(), &clear_value, 1, &clear_range); - } - - return new TCacheEntry(config, std::move(texture), framebuffer); + return VKTexture::Create(config); } bool TextureCache::CreateRenderPasses() @@ -351,264 +227,6 @@ bool TextureCache::CreateRenderPasses() return true; } -TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_, - std::unique_ptr<Texture2D> texture, - VkFramebuffer framebuffer) - : TCacheEntryBase(config_), m_texture(std::move(texture)), m_framebuffer(framebuffer) -{ -} - -TextureCache::TCacheEntry::~TCacheEntry() -{ - // Texture is automatically cleaned up, however, we don't want to leave it bound. - StateTracker::GetInstance()->UnbindTexture(m_texture->GetView()); - if (m_framebuffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer); -} - -void TextureCache::TCacheEntry::Load(u32 level, u32 width, u32 height, u32 row_length, - const u8* buffer, size_t buffer_size) -{ - // Can't copy data larger than the texture extents. - width = std::max(1u, std::min(width, m_texture->GetWidth() >> level)); - height = std::max(1u, std::min(height, m_texture->GetHeight() >> level)); - - // We don't care about the existing contents of the texture, so we could the image layout to - // VK_IMAGE_LAYOUT_UNDEFINED here. However, under section 2.2.1, Queue Operation of the Vulkan - // specification, it states: - // - // Command buffer submissions to a single queue must always adhere to command order and - // API order, but otherwise may overlap or execute out of order. - // - // Therefore, if a previous frame's command buffer is still sampling from this texture, and we - // overwrite it without a pipeline barrier, a texture sample could occur in parallel with the - // texture upload/copy. I'm not sure if any drivers currently take advantage of this, but we - // should insert an explicit pipeline barrier just in case (done by TransitionToLayout). - // - // We transition to TRANSFER_DST, ready for the image copy, and leave the texture in this state. - // When the last mip level is uploaded, we transition to SHADER_READ_ONLY, ready for use. This is - // because we can't transition in a render pass, and we don't necessarily know when this texture - // is going to be used. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // For unaligned textures, we can save some memory in the transfer buffer by skipping the rows - // that lie outside of the texture's dimensions. - 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(config.format, row_length); - size_t upload_size = source_pitch * num_rows; - std::unique_ptr<StagingBuffer> temp_buffer; - VkBuffer upload_buffer; - VkDeviceSize upload_buffer_offset; - - // Does this texture data fit within the streaming buffer? - if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD && - upload_size <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) - { - StreamBuffer* stream_buffer = TextureCache::GetInstance()->m_texture_upload_buffer.get(); - if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) - { - // Execute the command buffer first. - WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); - - // Try allocating again. This may cause a fence wait. - if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) - PanicAlert("Failed to allocate space in texture upload buffer"); - } - - // Copy to the streaming buffer. - upload_buffer = stream_buffer->GetBuffer(); - upload_buffer_offset = stream_buffer->GetCurrentOffset(); - std::memcpy(stream_buffer->GetCurrentHostPointer(), buffer, upload_size); - stream_buffer->CommitMemory(upload_size); - } - else - { - // Create a temporary staging buffer that is destroyed after the image is copied. - temp_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_UPLOAD, upload_size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - if (!temp_buffer || !temp_buffer->Map()) - { - PanicAlert("Failed to allocate staging texture for large texture upload."); - return; - } - - upload_buffer = temp_buffer->GetBuffer(); - upload_buffer_offset = 0; - temp_buffer->Write(0, buffer, upload_size, true); - temp_buffer->Unmap(); - } - - // Copy from the streaming buffer to the actual image. - VkBufferImageCopy image_copy = { - upload_buffer_offset, // VkDeviceSize bufferOffset - row_length, // uint32_t bufferRowLength - 0, // uint32_t bufferImageHeight - {VK_IMAGE_ASPECT_COLOR_BIT, level, 0, 1}, // VkImageSubresourceLayers imageSubresource - {0, 0, 0}, // VkOffset3D imageOffset - {width, height, 1} // VkExtent3D imageExtent - }; - vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), upload_buffer, - m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &image_copy); - - // Last mip level? We shouldn't be doing any further uploads now, so transition for rendering. - if (level == (config.levels - 1)) - { - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } -} - -void TextureCache::TCacheEntry::FromRenderTarget(bool is_depth_copy, const EFBRectangle& src_rect, - bool scale_by_half, unsigned int cbufid, - const float* colmat) -{ - // A better way of doing this would be nice. - FramebufferManager* framebuffer_mgr = - static_cast<FramebufferManager*>(g_framebuffer_manager.get()); - TargetRectangle scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); - - // Flush EFB pokes first, as they're expected to be included. - framebuffer_mgr->FlushEFBPokes(); - - // Has to be flagged as a render target. - _assert_(m_framebuffer != VK_NULL_HANDLE); - - // Can't be done in a render pass, since we're doing our own render pass! - VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); - StateTracker::GetInstance()->EndRenderPass(); - - // Transition EFB to shader resource before binding. - // An out-of-bounds source region is valid here, and fine for the draw (since it is converted - // to texture coordinates), but it's not valid to resolve an out-of-range rectangle. - VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top}, - {static_cast<u32>(scaled_src_rect.GetWidth()), - static_cast<u32>(scaled_src_rect.GetHeight())}}; - region = Util::ClampRect2D(region, FramebufferManager::GetInstance()->GetEFBWidth(), - FramebufferManager::GetInstance()->GetEFBHeight()); - Texture2D* src_texture; - if (is_depth_copy) - src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region); - else - src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region); - - VkSampler src_sampler = - scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler(); - VkImageLayout original_layout = src_texture->GetLayout(); - src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - UtilityShaderDraw draw( - command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), - TextureCache::GetInstance()->m_render_pass, g_object_cache->GetPassthroughVertexShader(), - g_object_cache->GetPassthroughGeometryShader(), - is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader : - TextureCache::GetInstance()->m_efb_color_to_tex_shader); - - draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28)); - draw.SetPSSampler(0, src_texture->GetView(), src_sampler); - - VkRect2D dest_region = {{0, 0}, {m_texture->GetWidth(), m_texture->GetHeight()}}; - - draw.BeginRenderPass(m_framebuffer, dest_region); - - draw.DrawQuad(0, 0, config.width, config.height, scaled_src_rect.left, scaled_src_rect.top, 0, - scaled_src_rect.GetWidth(), scaled_src_rect.GetHeight(), - framebuffer_mgr->GetEFBWidth(), framebuffer_mgr->GetEFBHeight()); - - draw.EndRenderPass(); - - // We touched everything, so put it back. - StateTracker::GetInstance()->SetPendingRebind(); - - // Transition the EFB back to its original layout. - src_texture->TransitionToLayout(command_buffer, original_layout); - - // Ensure texture is in SHADER_READ_ONLY layout, ready for usage. - m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - -void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle<int>& src_rect, - const MathUtil::Rectangle<int>& dst_rect) -{ - const TCacheEntry* source_vk = static_cast<const TCacheEntry*>(source); - TextureCache::GetInstance()->CopyRectangleFromTexture(this, dst_rect, source_vk->GetTexture(), - src_rect); - - // Ensure textures are ready for use again. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - source_vk->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - -void TextureCache::TCacheEntry::Bind(unsigned int stage) -{ - // Texture should always be in SHADER_READ_ONLY layout prior to use. - // This is so we don't need to transition during render passes. - _assert_(m_texture->GetLayout() == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); -} - -bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level) -{ - _assert_(level < config.levels); - - // We can't dump compressed textures currently (it would mean drawing them to a RGBA8 - // framebuffer, and saving that). TextureCache does not call Save for custom textures - // anyway, so this is fine for now. - _assert_(config.format == HostTextureFormat::RGBA8); - - // Determine dimensions of image we want to save. - u32 level_width = std::max(1u, config.width >> level); - u32 level_height = std::max(1u, config.height >> level); - - // Use a temporary staging texture for the download. Certainly not optimal, - // but since we have to idle the GPU anyway it doesn't really matter. - std::unique_ptr<StagingTexture2D> staging_texture = StagingTexture2D::Create( - STAGING_BUFFER_TYPE_READBACK, level_width, level_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. - staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, - level_width, level_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 (!staging_texture->Map()) - { - PanicAlert("Failed to map staging texture"); - return false; - } - - // Write texture out to file. - // It's okay to throw this texture away immediately, since we're done with it, and - // we blocked until the copy completed on the GPU anyway. - bool result = TextureToPng(reinterpret_cast<u8*>(staging_texture->GetMapPointer()), - static_cast<u32>(staging_texture->GetRowStride()), filename, - level_width, level_height); - - staging_texture->Unmap(); - return result; -} - bool TextureCache::CompileShaders() { static const char COPY_SHADER_SOURCE[] = R"( @@ -723,4 +341,76 @@ void TextureCache::DeleteShaders() } } +void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + const EFBRectangle& src_rect, bool scale_by_half, + unsigned int cbuf_id, const float* colmat) +{ + VKTexture* texture = static_cast<VKTexture*>(entry->texture.get()); + + // A better way of doing this would be nice. + FramebufferManager* framebuffer_mgr = + static_cast<FramebufferManager*>(g_framebuffer_manager.get()); + TargetRectangle scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); + + // Flush EFB pokes first, as they're expected to be included. + framebuffer_mgr->FlushEFBPokes(); + + // Has to be flagged as a render target. + _assert_(texture->GetFramebuffer() != VK_NULL_HANDLE); + + // Can't be done in a render pass, since we're doing our own render pass! + VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); + StateTracker::GetInstance()->EndRenderPass(); + + // Transition EFB to shader resource before binding. + // An out-of-bounds source region is valid here, and fine for the draw (since it is converted + // to texture coordinates), but it's not valid to resolve an out-of-range rectangle. + VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top}, + {static_cast<u32>(scaled_src_rect.GetWidth()), + static_cast<u32>(scaled_src_rect.GetHeight())}}; + region = Util::ClampRect2D(region, FramebufferManager::GetInstance()->GetEFBWidth(), + FramebufferManager::GetInstance()->GetEFBHeight()); + Texture2D* src_texture; + if (is_depth_copy) + src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region); + else + src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region); + + VkSampler src_sampler = + scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler(); + VkImageLayout original_layout = src_texture->GetLayout(); + src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + texture->GetRawTexIdentifier()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + UtilityShaderDraw draw(command_buffer, + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), + m_render_pass, g_object_cache->GetPassthroughVertexShader(), + g_object_cache->GetPassthroughGeometryShader(), + is_depth_copy ? m_efb_depth_to_tex_shader : m_efb_color_to_tex_shader); + + draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28)); + draw.SetPSSampler(0, src_texture->GetView(), src_sampler); + + VkRect2D dest_region = {{0, 0}, {texture->GetConfig().width, texture->GetConfig().height}}; + + draw.BeginRenderPass(texture->GetFramebuffer(), dest_region); + + draw.DrawQuad(0, 0, texture->GetConfig().width, texture->GetConfig().height, scaled_src_rect.left, + scaled_src_rect.top, 0, scaled_src_rect.GetWidth(), scaled_src_rect.GetHeight(), + framebuffer_mgr->GetEFBWidth(), framebuffer_mgr->GetEFBHeight()); + + draw.EndRenderPass(); + + // We touched everything, so put it back. + StateTracker::GetInstance()->SetPendingRebind(); + + // Transition the EFB back to its original layout. + src_texture->TransitionToLayout(command_buffer, original_layout); + + // Ensure texture is in SHADER_READ_ONLY layout, ready for usage. + texture->GetRawTexIdentifier()->TransitionToLayout(command_buffer, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + } // namespace Vulkan |
