summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
diff options
context:
space:
mode:
authorshuffle2 <godisgovernment@gmail.com>2017-06-18 12:57:05 -0700
committerGitHub <noreply@github.com>2017-06-18 12:57:05 -0700
commite63c33783049753784a24675bfa0792e54684fbd (patch)
tree3d984535025f4cb97489c9665220ac4237e7761c /Source/Core/VideoBackends/Vulkan/TextureCache.cpp
parent09457d3a4245ec9d4f2fdac3d1d0873a3354b858 (diff)
parente4896d39bd927b0b8e109e0334fa15cdb8e004f3 (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.cpp530
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