| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | // Copyright 2016 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/CommandBufferManager.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/Texture2D.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/VulkanContext.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Vulkan | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | Texture2D::Texture2D(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, | 
					
						
							|  |  |  |                      VkSampleCountFlagBits samples, VkImageViewType view_type, VkImage image, | 
					
						
							|  |  |  |                      VkDeviceMemory device_memory, VkImageView view) | 
					
						
							|  |  |  |     : m_width(width), m_height(height), m_levels(levels), m_layers(layers), m_format(format), | 
					
						
							|  |  |  |       m_samples(samples), m_view_type(view_type), m_image(image), m_device_memory(device_memory), | 
					
						
							|  |  |  |       m_view(view) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Texture2D::~Texture2D() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |   g_command_buffer_mgr->DeferImageViewDestruction(m_view); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
 | 
					
						
							|  |  |  |   if (m_device_memory != VK_NULL_HANDLE) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |     g_command_buffer_mgr->DeferImageDestruction(m_image); | 
					
						
							|  |  |  |     g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_device_memory); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::unique_ptr<Texture2D> Texture2D::Create(u32 width, u32 height, u32 levels, u32 layers, | 
					
						
							|  |  |  |                                              VkFormat format, VkSampleCountFlagBits samples, | 
					
						
							|  |  |  |                                              VkImageViewType view_type, VkImageTiling tiling, | 
					
						
							|  |  |  |                                              VkImageUsageFlags usage) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | 
					
						
							|  |  |  |                                   nullptr, | 
					
						
							|  |  |  |                                   0, | 
					
						
							|  |  |  |                                   VK_IMAGE_TYPE_2D, | 
					
						
							|  |  |  |                                   format, | 
					
						
							|  |  |  |                                   {width, height, 1}, | 
					
						
							|  |  |  |                                   levels, | 
					
						
							|  |  |  |                                   layers, | 
					
						
							|  |  |  |                                   samples, | 
					
						
							|  |  |  |                                   tiling, | 
					
						
							|  |  |  |                                   usage, | 
					
						
							|  |  |  |                                   VK_SHARING_MODE_EXCLUSIVE, | 
					
						
							|  |  |  |                                   0, | 
					
						
							|  |  |  |                                   nullptr, | 
					
						
							|  |  |  |                                   VK_IMAGE_LAYOUT_UNDEFINED}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkImage image = VK_NULL_HANDLE; | 
					
						
							|  |  |  |   VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image); | 
					
						
							|  |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Allocate memory to back this texture, we want device local memory in this case
 | 
					
						
							|  |  |  |   VkMemoryRequirements memory_requirements; | 
					
						
							|  |  |  |   vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkMemoryAllocateInfo memory_info = { | 
					
						
							|  |  |  |       VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size, | 
					
						
							|  |  |  |       g_vulkan_context->GetMemoryType(memory_requirements.memoryTypeBits, | 
					
						
							|  |  |  |                                       VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkDeviceMemory device_memory; | 
					
						
							|  |  |  |   res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory); | 
					
						
							|  |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); | 
					
						
							|  |  |  |     vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0); | 
					
						
							|  |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); | 
					
						
							|  |  |  |     vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); | 
					
						
							|  |  |  |     vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkImageViewCreateInfo view_info = { | 
					
						
							|  |  |  |       VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 
					
						
							|  |  |  |       nullptr, | 
					
						
							|  |  |  |       0, | 
					
						
							|  |  |  |       image, | 
					
						
							|  |  |  |       view_type, | 
					
						
							|  |  |  |       format, | 
					
						
							|  |  |  |       {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, | 
					
						
							|  |  |  |        VK_COMPONENT_SWIZZLE_IDENTITY}, | 
					
						
							|  |  |  |       {Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) : | 
					
						
							|  |  |  |                                      static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT), | 
					
						
							|  |  |  |        0, levels, 0, layers}}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkImageView view = VK_NULL_HANDLE; | 
					
						
							|  |  |  |   res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); | 
					
						
							|  |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     LOG_VULKAN_ERROR(res, "vkCreateImageView failed: "); | 
					
						
							|  |  |  |     vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); | 
					
						
							|  |  |  |     vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return std::make_unique<Texture2D>(width, height, levels, layers, format, samples, view_type, | 
					
						
							|  |  |  |                                      image, device_memory, view); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::unique_ptr<Texture2D> Texture2D::CreateFromExistingImage(u32 width, u32 height, u32 levels, | 
					
						
							|  |  |  |                                                               u32 layers, VkFormat format, | 
					
						
							|  |  |  |                                                               VkSampleCountFlagBits samples, | 
					
						
							|  |  |  |                                                               VkImageViewType view_type, | 
					
						
							|  |  |  |                                                               VkImage existing_image) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Only need to create the image view, this is mainly for swap chains.
 | 
					
						
							|  |  |  |   VkImageViewCreateInfo view_info = { | 
					
						
							|  |  |  |       VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 
					
						
							|  |  |  |       nullptr, | 
					
						
							|  |  |  |       0, | 
					
						
							|  |  |  |       existing_image, | 
					
						
							|  |  |  |       view_type, | 
					
						
							|  |  |  |       format, | 
					
						
							|  |  |  |       {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, | 
					
						
							|  |  |  |        VK_COMPONENT_SWIZZLE_IDENTITY}, | 
					
						
							|  |  |  |       {Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) : | 
					
						
							|  |  |  |                                      static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT), | 
					
						
							|  |  |  |        0, levels, 0, layers}}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |   // Memory is managed by the owner of the image.
 | 
					
						
							|  |  |  |   VkDeviceMemory memory = VK_NULL_HANDLE; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   VkImageView view = VK_NULL_HANDLE; | 
					
						
							|  |  |  |   VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); | 
					
						
							|  |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     LOG_VULKAN_ERROR(res, "vkCreateImageView failed: "); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return std::make_unique<Texture2D>(width, height, levels, layers, format, samples, view_type, | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |                                      existing_image, memory, view); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Texture2D::OverrideImageLayout(VkImageLayout new_layout) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_layout = new_layout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Texture2D::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (m_layout == new_layout) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkImageMemoryBarrier barrier = { | 
					
						
							|  |  |  |       VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,  // VkStructureType            sType
 | 
					
						
							|  |  |  |       nullptr,                                 // const void*                pNext
 | 
					
						
							|  |  |  |       0,                                       // VkAccessFlags              srcAccessMask
 | 
					
						
							|  |  |  |       0,                                       // VkAccessFlags              dstAccessMask
 | 
					
						
							|  |  |  |       m_layout,                                // VkImageLayout              oldLayout
 | 
					
						
							|  |  |  |       new_layout,                              // VkImageLayout              newLayout
 | 
					
						
							|  |  |  |       VK_QUEUE_FAMILY_IGNORED,                 // uint32_t                   srcQueueFamilyIndex
 | 
					
						
							|  |  |  |       VK_QUEUE_FAMILY_IGNORED,                 // uint32_t                   dstQueueFamilyIndex
 | 
					
						
							|  |  |  |       m_image,                                 // VkImage                    image
 | 
					
						
							|  |  |  |       {static_cast<VkImageAspectFlags>(Util::IsDepthFormat(m_format) ? VK_IMAGE_ASPECT_DEPTH_BIT : | 
					
						
							|  |  |  |                                                                        VK_IMAGE_ASPECT_COLOR_BIT), | 
					
						
							|  |  |  |        0, m_levels, 0, m_layers}  // VkImageSubresourceRange    subresourceRange
 | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // srcStageMask -> Stages that must complete before the barrier
 | 
					
						
							|  |  |  |   // dstStageMask -> Stages that must wait for after the barrier before beginning
 | 
					
						
							|  |  |  |   VkPipelineStageFlags srcStageMask, dstStageMask; | 
					
						
							|  |  |  |   switch (m_layout) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_UNDEFINED: | 
					
						
							|  |  |  |     // Layout undefined therefore contents undefined, and we don't care what happens to it.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = 0; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_PREINITIALIZED: | 
					
						
							|  |  |  |     // Image has been pre-initialized by the host, so ensure all writes have completed.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: | 
					
						
							|  |  |  |     // Image was being used as a color attachment, so ensure all writes have completed.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = | 
					
						
							|  |  |  |         VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: | 
					
						
							|  |  |  |     // Image was being used as a depthstencil attachment, so ensure all writes have completed.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = | 
					
						
							|  |  |  |         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: | 
					
						
							|  |  |  |     // Image was being used as a shader resource, make sure all reads have finished.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: | 
					
						
							|  |  |  |     // Image was being used as a copy source, ensure all reads have finished.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: | 
					
						
							|  |  |  |     // Image was being used as a copy destination, ensure all writes have finished.
 | 
					
						
							|  |  |  |     barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   default: | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (new_layout) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_UNDEFINED: | 
					
						
							|  |  |  |     barrier.dstAccessMask = 0; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: | 
					
						
							|  |  |  |     barrier.dstAccessMask = | 
					
						
							|  |  |  |         VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: | 
					
						
							|  |  |  |     barrier.dstAccessMask = | 
					
						
							|  |  |  |         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: | 
					
						
							|  |  |  |     // TODO: Can we use FRAGMENT_SHADER here? We don't sample textures in the earlier stages.
 | 
					
						
							|  |  |  |     barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: | 
					
						
							|  |  |  |     barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: | 
					
						
							|  |  |  |     barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: | 
					
						
							|  |  |  |     barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; | 
					
						
							|  |  |  |     srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   default: | 
					
						
							|  |  |  |     dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, | 
					
						
							|  |  |  |                        &barrier); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_layout = new_layout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace Vulkan
 |