| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | // Copyright 2016 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | #include "VideoBackends/Vulkan/TextureCache.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <cstring>
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | #include <string>
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Common/Assert.h"
 | 
					
						
							|  |  |  | #include "Common/CommonFuncs.h"
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							|  |  |  | #include "Common/MsgHandler.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/CommandBufferManager.h"
 | 
					
						
							|  |  |  | #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"
 | 
					
						
							| 
									
										
										
										
											2016-11-19 23:25:23 +10:00
										 |  |  | #include "VideoBackends/Vulkan/TextureConverter.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoBackends/Vulkan/Util.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/VulkanContext.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "VideoCommon/ImageWrite.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Vulkan | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | TextureCache::TextureCache() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TextureCache::~TextureCache() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   if (m_render_pass != VK_NULL_HANDLE) | 
					
						
							|  |  |  |     vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_render_pass, nullptr); | 
					
						
							| 
									
										
										
										
											2016-10-01 22:22:14 +10:00
										 |  |  |   TextureCache::DeleteShaders(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  | TextureCache* TextureCache::GetInstance() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return static_cast<TextureCache*>(g_texture_cache.get()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool TextureCache::Initialize() | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							|  |  |  |   m_texture_upload_buffer = | 
					
						
							|  |  |  |       StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE, | 
					
						
							|  |  |  |                            MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE); | 
					
						
							|  |  |  |   if (!m_texture_upload_buffer) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to create texture upload buffer"); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!CreateRenderPasses()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to create copy render pass"); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-19 23:25:23 +10:00
										 |  |  |   m_texture_converter = std::make_unique<TextureConverter>(); | 
					
						
							|  |  |  |   if (!m_texture_converter->Initialize()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-11-19 23:25:23 +10:00
										 |  |  |     PanicAlert("Failed to initialize texture converter"); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!CompileShaders()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to compile one or more shaders"); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* base_unconverted, | 
					
						
							|  |  |  |                                   void* palette, TlutFormat format) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   TCacheEntry* entry = static_cast<TCacheEntry*>(base_entry); | 
					
						
							|  |  |  |   TCacheEntry* unconverted = static_cast<TCacheEntry*>(base_unconverted); | 
					
						
							| 
									
										
										
										
											2016-10-04 22:25:35 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   m_texture_converter->ConvertTexture(entry, unconverted, m_render_pass, palette, format); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TextureCache::CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, | 
					
						
							| 
									
										
										
										
											2016-12-26 20:54:37 +01:00
										 |  |  |                            u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |                            const EFBRectangle& src_rect, bool is_intensity, bool scale_by_half) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Flush EFB pokes first, as they're expected to be included.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   FramebufferManager::GetInstance()->FlushEFBPokes(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // MSAA case where we need to resolve first.
 | 
					
						
							|  |  |  |   // TODO: Do in one pass.
 | 
					
						
							|  |  |  |   TargetRectangle scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); | 
					
						
							|  |  |  |   VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top}, | 
					
						
							|  |  |  |                      {static_cast<u32>(scaled_src_rect.GetWidth()), | 
					
						
							|  |  |  |                       static_cast<u32>(scaled_src_rect.GetHeight())}}; | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   Texture2D* src_texture; | 
					
						
							| 
									
										
										
										
											2016-12-26 20:54:37 +01:00
										 |  |  |   if (is_depth_copy) | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-30 22:34:36 +10:00
										 |  |  |   // End render pass before barrier (since we have no self-dependencies).
 | 
					
						
							|  |  |  |   // The barrier has to happen after the render pass, not inside it, as we are going to be
 | 
					
						
							|  |  |  |   // reading from the texture immediately afterwards.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->EndRenderPass(); | 
					
						
							|  |  |  |   StateTracker::GetInstance()->OnReadback(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Transition to shader resource before reading.
 | 
					
						
							|  |  |  |   VkImageLayout original_layout = src_texture->GetLayout(); | 
					
						
							|  |  |  |   src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							|  |  |  |                                   VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-19 23:25:23 +10:00
										 |  |  |   m_texture_converter->EncodeTextureToMemory(src_texture->GetView(), dst, format, native_width, | 
					
						
							| 
									
										
										
										
											2016-12-26 20:54:37 +01:00
										 |  |  |                                              bytes_per_row, num_blocks_y, memory_stride, | 
					
						
							|  |  |  |                                              is_depth_copy, is_intensity, scale_by_half, src_rect); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Transition back to original state
 | 
					
						
							|  |  |  |   src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 21:19:09 +10:00
										 |  |  | 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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 SHADER_READ_ONLY state.
 | 
					
						
							|  |  |  |   src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							|  |  |  |                                   VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); | 
					
						
							|  |  |  |   dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |                                                 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); | 
					
						
							| 
									
										
										
										
											2016-10-22 21:19:09 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |                          g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), m_render_pass, | 
					
						
							| 
									
										
										
										
											2016-10-22 21:19:09 +10:00
										 |  |  |                          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(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& 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
 | 
					
						
							|  |  |  |   std::unique_ptr<Texture2D> texture = Texture2D::Create( | 
					
						
							|  |  |  |       config.width, config.height, config.levels, config.layers, TEXTURECACHE_TEXTURE_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, | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |         m_render_pass, | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |         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); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   return new TCacheEntry(config, std::move(texture), framebuffer); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool TextureCache::CreateRenderPasses() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   static constexpr VkAttachmentDescription update_attachment = { | 
					
						
							|  |  |  |       0, | 
					
						
							|  |  |  |       TEXTURECACHE_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, | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | 
					
						
							|  |  |  |       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   static constexpr VkAttachmentReference color_attachment_reference = { | 
					
						
							|  |  |  |       0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static constexpr VkSubpassDescription subpass_description = { | 
					
						
							|  |  |  |       0,       VK_PIPELINE_BIND_POINT_GRAPHICS, | 
					
						
							|  |  |  |       0,       nullptr, | 
					
						
							|  |  |  |       1,       &color_attachment_reference, | 
					
						
							|  |  |  |       nullptr, nullptr, | 
					
						
							|  |  |  |       0,       nullptr}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkRenderPassCreateInfo update_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | 
					
						
							|  |  |  |                                         nullptr, | 
					
						
							|  |  |  |                                         0, | 
					
						
							|  |  |  |                                         1, | 
					
						
							|  |  |  |                                         &update_attachment, | 
					
						
							|  |  |  |                                         1, | 
					
						
							|  |  |  |                                         &subpass_description, | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |                                         0, | 
					
						
							|  |  |  |                                         nullptr}; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   VkResult res = | 
					
						
							|  |  |  |       vkCreateRenderPass(g_vulkan_context->GetDevice(), &update_info, nullptr, &m_render_pass); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |     LOG_VULKAN_ERROR(res, "vkCreateRenderPass failed: "); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  | TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_, | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |                                        std::unique_ptr<Texture2D> texture, | 
					
						
							|  |  |  |                                        VkFramebuffer framebuffer) | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     : TCacheEntryBase(config_), m_texture(std::move(texture)), m_framebuffer(framebuffer) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TextureCache::TCacheEntry::~TCacheEntry() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   // Texture is automatically cleaned up, however, we don't want to leave it bound.
 | 
					
						
							|  |  |  |   StateTracker::GetInstance()->UnbindTexture(m_texture->GetView()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   if (m_framebuffer != VK_NULL_HANDLE) | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |     g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-06 18:57:58 -04:00
										 |  |  | void TextureCache::TCacheEntry::Load(const u8* buffer, unsigned int width, unsigned int height, | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |                                      unsigned int expanded_width, unsigned int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // 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)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   // 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.
 | 
					
						
							|  |  |  |   // This is so that the remaining mip levels can be uploaded without barriers, and then when the
 | 
					
						
							|  |  |  |   // texture is used, it can be transitioned to SHADER_READ_ONLY (see TCacheEntry::Bind).
 | 
					
						
							|  |  |  |   m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), | 
					
						
							|  |  |  |                                 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Does this texture data fit within the streaming buffer?
 | 
					
						
							|  |  |  |   u32 upload_width = width; | 
					
						
							|  |  |  |   u32 upload_pitch = upload_width * sizeof(u32); | 
					
						
							|  |  |  |   u32 upload_size = upload_pitch * height; | 
					
						
							|  |  |  |   u32 upload_alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity()); | 
					
						
							|  |  |  |   u32 source_pitch = expanded_width * 4; | 
					
						
							|  |  |  |   if ((upload_size + upload_alignment) <= STAGING_TEXTURE_UPLOAD_THRESHOLD && | 
					
						
							|  |  |  |       (upload_size + upload_alignment) <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // Assume tightly packed rows, with no padding as the buffer source.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     StreamBuffer* upload_buffer = TextureCache::GetInstance()->m_texture_upload_buffer.get(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Allocate memory from the streaming buffer for the texture data.
 | 
					
						
							|  |  |  |     if (!upload_buffer->ReserveMemory(upload_size, g_vulkan_context->GetBufferImageGranularity())) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // Execute the command buffer first.
 | 
					
						
							|  |  |  |       WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |       Util::ExecuteCurrentCommandsAndRestoreState(false); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Try allocating again. This may cause a fence wait.
 | 
					
						
							|  |  |  |       if (!upload_buffer->ReserveMemory(upload_size, g_vulkan_context->GetBufferImageGranularity())) | 
					
						
							|  |  |  |         PanicAlert("Failed to allocate space in texture upload buffer"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Grab buffer pointers
 | 
					
						
							|  |  |  |     VkBuffer image_upload_buffer = upload_buffer->GetBuffer(); | 
					
						
							|  |  |  |     VkDeviceSize image_upload_buffer_offset = upload_buffer->GetCurrentOffset(); | 
					
						
							|  |  |  |     u8* image_upload_buffer_pointer = upload_buffer->GetCurrentHostPointer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Copy to the buffer using the stride from the subresource layout
 | 
					
						
							| 
									
										
										
										
											2016-09-06 18:57:58 -04:00
										 |  |  |     const u8* source_ptr = buffer; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     if (upload_pitch != source_pitch) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       VkDeviceSize copy_pitch = std::min(source_pitch, upload_pitch); | 
					
						
							|  |  |  |       for (unsigned int row = 0; row < height; row++) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         memcpy(image_upload_buffer_pointer + row * upload_pitch, source_ptr + row * source_pitch, | 
					
						
							|  |  |  |                copy_pitch); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // Can copy the whole thing in one block, the pitch matches
 | 
					
						
							|  |  |  |       memcpy(image_upload_buffer_pointer, source_ptr, upload_size); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Flush buffer memory if necessary
 | 
					
						
							|  |  |  |     upload_buffer->CommitMemory(upload_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Copy from the streaming buffer to the actual image.
 | 
					
						
							|  |  |  |     VkBufferImageCopy image_copy = { | 
					
						
							|  |  |  |         image_upload_buffer_offset,                // VkDeviceSize                bufferOffset
 | 
					
						
							|  |  |  |         0,                                         // 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(), image_upload_buffer, | 
					
						
							|  |  |  |                            m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, | 
					
						
							|  |  |  |                            &image_copy); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // Slow path. The data for the image is too large to fit in the streaming buffer, so we need
 | 
					
						
							|  |  |  |     // to allocate a temporary texture to store the data in, then copy to the real texture.
 | 
					
						
							|  |  |  |     std::unique_ptr<StagingTexture2D> staging_texture = StagingTexture2D::Create( | 
					
						
							|  |  |  |         STAGING_BUFFER_TYPE_UPLOAD, width, height, TEXTURECACHE_TEXTURE_FORMAT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!staging_texture || !staging_texture->Map()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       PanicAlert("Failed to allocate staging texture for large texture upload."); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Copy data to staging texture first, then to the "real" texture.
 | 
					
						
							| 
									
										
										
										
											2016-09-06 18:57:58 -04:00
										 |  |  |     staging_texture->WriteTexels(0, 0, width, height, buffer, source_pitch); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     staging_texture->CopyToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), | 
					
						
							|  |  |  |                                  m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width, | 
					
						
							|  |  |  |                                  height, level, 0); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-26 21:33:57 +01:00
										 |  |  | void TextureCache::TCacheEntry::FromRenderTarget(bool is_depth_copy, const EFBRectangle& src_rect, | 
					
						
							|  |  |  |                                                  bool scale_by_half, unsigned int cbufid, | 
					
						
							|  |  |  |                                                  const float* colmat) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							|  |  |  |   // 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.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   framebuffer_mgr->FlushEFBPokes(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // 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(); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->EndRenderPass(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Transition EFB to shader resource before binding
 | 
					
						
							|  |  |  |   VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top}, | 
					
						
							|  |  |  |                      {static_cast<u32>(scaled_src_rect.GetWidth()), | 
					
						
							|  |  |  |                       static_cast<u32>(scaled_src_rect.GetHeight())}}; | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   Texture2D* src_texture; | 
					
						
							|  |  |  |   if (is_depth_copy) | 
					
						
							|  |  |  |     src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   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); | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   UtilityShaderDraw draw( | 
					
						
							| 
									
										
										
										
											2016-11-13 15:24:55 +10:00
										 |  |  |       command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT), | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |       TextureCache::GetInstance()->m_render_pass, g_object_cache->GetPassthroughVertexShader(), | 
					
						
							|  |  |  |       g_object_cache->GetPassthroughGeometryShader(), | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |       is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader : | 
					
						
							|  |  |  |                       TextureCache::GetInstance()->m_efb_color_to_tex_shader); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->SetPendingRebind(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Transition the EFB back to its original layout.
 | 
					
						
							|  |  |  |   src_texture->TransitionToLayout(command_buffer, original_layout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Render pass transitions texture to SHADER_READ_ONLY.
 | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							| 
									
										
										
										
											2016-10-22 21:19:09 +10:00
										 |  |  |   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); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TextureCache::TCacheEntry::Bind(unsigned int stage) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-11-21 01:59:19 +10:00
										 |  |  |   m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							|  |  |  |                                 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   _assert_(level < config.levels); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // 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); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->EndRenderPass(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // 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.
 | 
					
						
							| 
									
										
										
										
											2016-11-30 22:34:36 +10:00
										 |  |  |   Util::ExecuteCurrentCommandsAndRestoreState(false, true); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Map the staging texture so we can copy the contents out.
 | 
					
						
							| 
									
										
										
										
											2016-12-04 00:23:34 +10:00
										 |  |  |   if (!staging_texture->Map()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     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()), | 
					
						
							|  |  |  |                              staging_texture->GetRowStride(), filename, level_width, level_height); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   staging_texture->Unmap(); | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool TextureCache::CompileShaders() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   static const char COPY_SHADER_SOURCE[] = R"( | 
					
						
							|  |  |  |     layout(set = 1, binding = 0) uniform sampler2DArray samp0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     layout(location = 0) in float3 uv0; | 
					
						
							|  |  |  |     layout(location = 1) in float4 col0; | 
					
						
							|  |  |  |     layout(location = 0) out float4 ocol0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void main() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       ocol0 = texture(samp0, uv0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   )"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static const char EFB_COLOR_TO_TEX_SOURCE[] = R"( | 
					
						
							|  |  |  |     SAMPLER_BINDING(0) uniform sampler2DArray samp0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     layout(std140, push_constant) uniform PSBlock | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       vec4 colmat[7]; | 
					
						
							|  |  |  |     } C; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     layout(location = 0) in vec3 uv0; | 
					
						
							|  |  |  |     layout(location = 1) in vec4 col0; | 
					
						
							|  |  |  |     layout(location = 0) out vec4 ocol0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void main() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       float4 texcol = texture(samp0, uv0); | 
					
						
							| 
									
										
										
										
											2016-12-14 23:58:23 +10:00
										 |  |  |       texcol = floor(texcol * C.colmat[5]) * C.colmat[6]; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |       ocol0 = texcol * mat4(C.colmat[0], C.colmat[1], C.colmat[2], C.colmat[3]) + C.colmat[4]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   )"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static const char EFB_DEPTH_TO_TEX_SOURCE[] = R"( | 
					
						
							|  |  |  |     SAMPLER_BINDING(0) uniform sampler2DArray samp0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     layout(std140, push_constant) uniform PSBlock | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       vec4 colmat[5]; | 
					
						
							|  |  |  |     } C; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     layout(location = 0) in vec3 uv0; | 
					
						
							|  |  |  |     layout(location = 1) in vec4 col0; | 
					
						
							|  |  |  |     layout(location = 0) out vec4 ocol0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void main() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       #if MONO_DEPTH
 | 
					
						
							|  |  |  |         vec4 texcol = texture(samp0, vec3(uv0.xy, 0.0f)); | 
					
						
							|  |  |  |       #else
 | 
					
						
							|  |  |  |         vec4 texcol = texture(samp0, uv0); | 
					
						
							|  |  |  |       #endif
 | 
					
						
							|  |  |  |       int depth = int((1.0 - texcol.x) * 16777216.0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Convert to Z24 format
 | 
					
						
							|  |  |  |       ivec4 workspace; | 
					
						
							|  |  |  |       workspace.r = (depth >> 16) & 255; | 
					
						
							|  |  |  |       workspace.g = (depth >> 8) & 255; | 
					
						
							|  |  |  |       workspace.b = depth & 255; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Convert to Z4 format
 | 
					
						
							|  |  |  |       workspace.a = (depth >> 16) & 0xF0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Normalize components to [0.0..1.0]
 | 
					
						
							|  |  |  |       texcol = vec4(workspace) / 255.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ocol0 = texcol * mat4(C.colmat[0], C.colmat[1], C.colmat[2], C.colmat[3]) + C.colmat[4]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   )"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::string header = g_object_cache->GetUtilityShaderHeader(); | 
					
						
							|  |  |  |   std::string source; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   source = header + COPY_SHADER_SOURCE; | 
					
						
							|  |  |  |   m_copy_shader = Util::CompileAndCreateFragmentShader(source); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   source = header + EFB_COLOR_TO_TEX_SOURCE; | 
					
						
							|  |  |  |   m_efb_color_to_tex_shader = Util::CompileAndCreateFragmentShader(source); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (g_ActiveConfig.bStereoEFBMonoDepth) | 
					
						
							|  |  |  |     source = header + "#define MONO_DEPTH 1\n" + EFB_DEPTH_TO_TEX_SOURCE; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     source = header + EFB_DEPTH_TO_TEX_SOURCE; | 
					
						
							|  |  |  |   m_efb_depth_to_tex_shader = Util::CompileAndCreateFragmentShader(source); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 00:43:50 +10:00
										 |  |  |   return m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE && | 
					
						
							|  |  |  |          m_efb_depth_to_tex_shader != VK_NULL_HANDLE; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TextureCache::DeleteShaders() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-01 22:22:14 +10:00
										 |  |  |   // It is safe to destroy shader modules after they are consumed by creating a pipeline.
 | 
					
						
							|  |  |  |   // Therefore, no matter where this function is called from, it won't cause an issue due to
 | 
					
						
							|  |  |  |   // pending commands, although at the time of writing should only be called at the end of
 | 
					
						
							|  |  |  |   // a frame. See Vulkan spec, section 2.3.1. Object Lifetime.
 | 
					
						
							|  |  |  |   if (m_copy_shader != VK_NULL_HANDLE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_copy_shader, nullptr); | 
					
						
							|  |  |  |     m_copy_shader = VK_NULL_HANDLE; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (m_efb_color_to_tex_shader != VK_NULL_HANDLE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_color_to_tex_shader, nullptr); | 
					
						
							|  |  |  |     m_efb_color_to_tex_shader = VK_NULL_HANDLE; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (m_efb_depth_to_tex_shader != VK_NULL_HANDLE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr); | 
					
						
							|  |  |  |     m_efb_depth_to_tex_shader = VK_NULL_HANDLE; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace Vulkan
 |