duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

vulkan_texture.cpp (45997B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "vulkan_texture.h"
      5 #include "vulkan_builders.h"
      6 #include "vulkan_device.h"
      7 
      8 #include "common/align.h"
      9 #include "common/assert.h"
     10 #include "common/bitutils.h"
     11 #include "common/log.h"
     12 
     13 Log_SetChannel(VulkanDevice);
     14 
     15 static constexpr const VkComponentMapping s_identity_swizzle{
     16   VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
     17   VK_COMPONENT_SWIZZLE_IDENTITY};
     18 
     19 static VkImageLayout GetVkImageLayout(VulkanTexture::Layout layout)
     20 {
     21   // TODO: Wrong for depth textures in feedback loop
     22   static constexpr std::array<VkImageLayout, static_cast<u32>(VulkanTexture::Layout::Count)> s_vk_layout_mapping = {{
     23     VK_IMAGE_LAYOUT_UNDEFINED,                        // Undefined
     24     VK_IMAGE_LAYOUT_PREINITIALIZED,                   // Preinitialized
     25     VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,         // ColorAttachment
     26     VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // DepthStencilAttachment
     27     VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,         // ShaderReadOnly
     28     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,             // ClearDst
     29     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,             // TransferSrc
     30     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,             // TransferDst
     31     VK_IMAGE_LAYOUT_GENERAL,                          // TransferSelf
     32     VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,                  // PresentSrc
     33     VK_IMAGE_LAYOUT_GENERAL,                          // FeedbackLoop
     34     VK_IMAGE_LAYOUT_GENERAL,                          // ReadWriteImage
     35     VK_IMAGE_LAYOUT_GENERAL,                          // ComputeReadWriteImage
     36     VK_IMAGE_LAYOUT_GENERAL,                          // General
     37   }};
     38   return (layout == VulkanTexture::Layout::FeedbackLoop &&
     39           VulkanDevice::GetInstance().GetOptionalExtensions().vk_khr_dynamic_rendering_local_read) ?
     40            VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
     41            s_vk_layout_mapping[static_cast<u32>(layout)];
     42 }
     43 
     44 VulkanTexture::VulkanTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
     45                              VkImage image, VmaAllocation allocation, VkImageView view, VkFormat vk_format)
     46   : GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
     47                static_cast<u8>(samples), type, format),
     48     m_image(image), m_allocation(allocation), m_view(view), m_vk_format(vk_format)
     49 {
     50 }
     51 
     52 VulkanTexture::~VulkanTexture()
     53 {
     54   Destroy(true);
     55 }
     56 
     57 std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
     58                                                      Type type, Format format, VkFormat vk_format)
     59 {
     60   if (!ValidateConfig(width, height, layers, levels, samples, type, format))
     61     return {};
     62 
     63   VulkanDevice& dev = VulkanDevice::GetInstance();
     64 
     65   VkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
     66                            nullptr,
     67                            0,
     68                            VK_IMAGE_TYPE_2D,
     69                            vk_format,
     70                            {width, height, 1u},
     71                            levels,
     72                            layers,
     73                            static_cast<VkSampleCountFlagBits>(samples),
     74                            VK_IMAGE_TILING_OPTIMAL,
     75                            0u,
     76                            VK_SHARING_MODE_EXCLUSIVE,
     77                            0,
     78                            nullptr,
     79                            VK_IMAGE_LAYOUT_UNDEFINED};
     80 
     81   VmaAllocationCreateInfo aci = {};
     82   aci.usage = VMA_MEMORY_USAGE_GPU_ONLY;
     83   aci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
     84   aci.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
     85 
     86   VkImageViewCreateInfo vci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
     87                                nullptr,
     88                                0,
     89                                VK_NULL_HANDLE,
     90                                VK_IMAGE_VIEW_TYPE_2D,
     91                                vk_format,
     92                                s_identity_swizzle,
     93                                {VK_IMAGE_ASPECT_COLOR_BIT, 0, static_cast<u32>(levels), 0, 1}};
     94 
     95   // TODO: Don't need the feedback loop stuff yet.
     96   switch (type)
     97   {
     98     case Type::Texture:
     99     case Type::DynamicTexture:
    100     {
    101       ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
    102     }
    103     break;
    104 
    105     case Type::RenderTarget:
    106     {
    107       DebugAssert(levels == 1);
    108       ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
    109                   VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
    110                   VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
    111     }
    112     break;
    113 
    114     case Type::DepthStencil:
    115     {
    116       DebugAssert(levels == 1);
    117       ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
    118                   VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
    119       vci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
    120     }
    121     break;
    122 
    123     case Type::RWTexture:
    124     {
    125       DebugAssert(levels == 1);
    126       ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
    127                   VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
    128                   VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
    129     }
    130     break;
    131 
    132     default:
    133       return {};
    134   }
    135 
    136   // Use dedicated allocations for typical RT size
    137   if ((type == Type::RenderTarget || type == Type::DepthStencil) && width >= 512 && height >= 448)
    138     aci.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
    139 
    140   VkImage image = VK_NULL_HANDLE;
    141   VmaAllocation allocation = VK_NULL_HANDLE;
    142   VkResult res = vmaCreateImage(dev.GetAllocator(), &ici, &aci, &image, &allocation, nullptr);
    143   if (aci.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT && res != VK_SUCCESS)
    144   {
    145     // try without dedicated allocation
    146     aci.flags &= ~VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
    147     res = vmaCreateImage(dev.GetAllocator(), &ici, &aci, &image, &allocation, nullptr);
    148   }
    149   if (res == VK_ERROR_OUT_OF_DEVICE_MEMORY)
    150   {
    151     ERROR_LOG("Failed to allocate device memory for {}x{} texture", width, height);
    152     return {};
    153   }
    154   else if (res != VK_SUCCESS)
    155   {
    156     LOG_VULKAN_ERROR(res, "vmaCreateImage failed: ");
    157     return {};
    158   }
    159 
    160   VkImageView view = VK_NULL_HANDLE;
    161   vci.image = image;
    162   res = vkCreateImageView(dev.GetVulkanDevice(), &vci, nullptr, &view);
    163   if (res != VK_SUCCESS)
    164   {
    165     LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
    166     vmaDestroyImage(dev.GetAllocator(), image, allocation);
    167     return {};
    168   }
    169 
    170   return std::unique_ptr<VulkanTexture>(
    171     new VulkanTexture(width, height, layers, levels, samples, type, format, image, allocation, view, vk_format));
    172 }
    173 
    174 void VulkanTexture::Destroy(bool defer)
    175 {
    176   VulkanDevice& dev = VulkanDevice::GetInstance();
    177   dev.UnbindTexture(this);
    178   if (defer)
    179   {
    180     for (auto& it : m_descriptor_sets)
    181       dev.DeferPersistentDescriptorSetDestruction(it.second);
    182   }
    183   else
    184   {
    185     for (auto& it : m_descriptor_sets)
    186       dev.FreePersistentDescriptorSet(it.second);
    187   }
    188   m_descriptor_sets.clear();
    189 
    190   if (m_view != VK_NULL_HANDLE)
    191   {
    192     if (defer)
    193       VulkanDevice::GetInstance().DeferImageViewDestruction(m_view);
    194     else
    195       vkDestroyImageView(VulkanDevice::GetInstance().GetVulkanDevice(), m_view, nullptr);
    196     m_view = VK_NULL_HANDLE;
    197   }
    198 
    199   // If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
    200   if (m_allocation != VK_NULL_HANDLE)
    201   {
    202     if (defer)
    203       VulkanDevice::GetInstance().DeferImageDestruction(m_image, m_allocation);
    204     else
    205       vmaDestroyImage(VulkanDevice::GetInstance().GetAllocator(), m_image, m_allocation);
    206     m_image = VK_NULL_HANDLE;
    207     m_allocation = VK_NULL_HANDLE;
    208   }
    209 }
    210 
    211 VkImageLayout VulkanTexture::GetVkLayout() const
    212 {
    213   return GetVkImageLayout(m_layout);
    214 }
    215 
    216 VkClearColorValue VulkanTexture::GetClearColorValue() const
    217 {
    218   VkClearColorValue ccv;
    219   std::memcpy(ccv.float32, GetUNormClearColor().data(), sizeof(ccv.float32));
    220   return ccv;
    221 }
    222 
    223 VkClearDepthStencilValue VulkanTexture::GetClearDepthValue() const
    224 {
    225   return VkClearDepthStencilValue{m_clear_value.depth, 0u};
    226 }
    227 
    228 VkCommandBuffer VulkanTexture::GetCommandBufferForUpdate()
    229 {
    230   VulkanDevice& dev = VulkanDevice::GetInstance();
    231   if ((m_type != Type::Texture && m_type != Type::DynamicTexture) ||
    232       m_use_fence_counter == dev.GetCurrentFenceCounter())
    233   {
    234     // Console.WriteLn("Texture update within frame, can't use do beforehand");
    235     if (dev.InRenderPass())
    236       dev.EndRenderPass();
    237     return dev.GetCurrentCommandBuffer();
    238   }
    239 
    240   return dev.GetCurrentInitCommandBuffer();
    241 }
    242 
    243 void VulkanTexture::CopyTextureDataForUpload(void* dst, const void* src, u32 width, u32 height, u32 pitch,
    244                                              u32 upload_pitch) const
    245 {
    246   StringUtil::StrideMemCpy(dst, upload_pitch, src, pitch, GetPixelSize() * width, height);
    247 }
    248 
    249 VkBuffer VulkanTexture::AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 width,
    250                                                     u32 height) const
    251 {
    252   const u32 size = upload_pitch * height;
    253   const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
    254                                   nullptr,
    255                                   0,
    256                                   static_cast<VkDeviceSize>(size),
    257                                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
    258                                   VK_SHARING_MODE_EXCLUSIVE,
    259                                   0,
    260                                   nullptr};
    261 
    262   // Don't worry about setting the coherent bit for this upload, the main reason we had
    263   // that set in StreamBuffer was for MoltenVK, which would upload the whole buffer on
    264   // smaller uploads, but we're writing to the whole thing anyway.
    265   VmaAllocationCreateInfo aci = {};
    266   aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
    267   aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
    268 
    269   VmaAllocationInfo ai;
    270   VkBuffer buffer;
    271   VmaAllocation allocation;
    272   VkResult res = vmaCreateBuffer(VulkanDevice::GetInstance().GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
    273   if (res != VK_SUCCESS)
    274   {
    275     LOG_VULKAN_ERROR(res, "(AllocateUploadStagingBuffer) vmaCreateBuffer() failed: ");
    276     return VK_NULL_HANDLE;
    277   }
    278 
    279   // Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
    280   VulkanDevice::GetInstance().DeferBufferDestruction(buffer, allocation);
    281 
    282   // And write the data.
    283   CopyTextureDataForUpload(ai.pMappedData, data, width, height, pitch, upload_pitch);
    284   vmaFlushAllocation(VulkanDevice::GetInstance().GetAllocator(), allocation, 0, size);
    285   return buffer;
    286 }
    287 
    288 void VulkanTexture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 x, u32 y, u32 width, u32 height, u32 layer, u32 level,
    289                                      u32 pitch, VkBuffer buffer, u32 buffer_offset)
    290 {
    291   const Layout old_layout = m_layout;
    292   if (old_layout != Layout::TransferDst)
    293     TransitionSubresourcesToLayout(cmdbuf, layer, 1, level, 1, old_layout, Layout::TransferDst);
    294 
    295   const u32 row_length = pitch / GetPixelSize();
    296 
    297   const VkBufferImageCopy bic = {static_cast<VkDeviceSize>(buffer_offset),
    298                                  row_length,
    299                                  height,
    300                                  {VK_IMAGE_ASPECT_COLOR_BIT, static_cast<u32>(level), 0u, 1u},
    301                                  {static_cast<s32>(x), static_cast<s32>(y), 0},
    302                                  {width, height, 1u}};
    303 
    304   vkCmdCopyBufferToImage(cmdbuf, buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bic);
    305 
    306   if (old_layout != Layout::TransferDst)
    307     TransitionSubresourcesToLayout(cmdbuf, layer, 1, level, 1, Layout::TransferDst, old_layout);
    308 }
    309 
    310 bool VulkanTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer, u32 level)
    311 {
    312   DebugAssert(layer < m_layers && level < m_levels);
    313   DebugAssert((x + width) <= GetMipWidth(level) && (y + height) <= GetMipHeight(level));
    314 
    315   const u32 upload_pitch = Common::AlignUpPow2(pitch, VulkanDevice::GetInstance().GetBufferCopyRowPitchAlignment());
    316   const u32 required_size = height * upload_pitch;
    317   VulkanDevice& dev = VulkanDevice::GetInstance();
    318   VulkanStreamBuffer& sbuffer = dev.GetTextureUploadBuffer();
    319 
    320   // If the texture is larger than half our streaming buffer size, use a separate buffer.
    321   // Otherwise allocation will either fail, or require lots of cmdbuffer submissions.
    322   VkBuffer buffer;
    323   u32 buffer_offset;
    324   if (required_size > (sbuffer.GetCurrentSize() / 2))
    325   {
    326     buffer_offset = 0;
    327     buffer = AllocateUploadStagingBuffer(data, pitch, upload_pitch, width, height);
    328     if (buffer == VK_NULL_HANDLE)
    329       return false;
    330   }
    331   else
    332   {
    333     if (!sbuffer.ReserveMemory(required_size, dev.GetBufferCopyOffsetAlignment())) [[unlikely]]
    334     {
    335       dev.SubmitCommandBuffer(false, TinyString::from_format("Needs {} bytes in texture upload buffer", required_size));
    336       if (!sbuffer.ReserveMemory(required_size, dev.GetBufferCopyOffsetAlignment())) [[unlikely]]
    337       {
    338         ERROR_LOG("Failed to reserve texture upload memory ({} bytes).", required_size);
    339         return false;
    340       }
    341     }
    342 
    343     buffer = sbuffer.GetBuffer();
    344     buffer_offset = sbuffer.GetCurrentOffset();
    345     CopyTextureDataForUpload(sbuffer.GetCurrentHostPointer(), data, width, height, pitch, upload_pitch);
    346     sbuffer.CommitMemory(required_size);
    347   }
    348 
    349   GPUDevice::GetStatistics().buffer_streamed += required_size;
    350   GPUDevice::GetStatistics().num_uploads++;
    351 
    352   const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
    353 
    354   // if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
    355   if (m_type == Type::RenderTarget)
    356   {
    357     if (m_state == State::Cleared && (x != 0 || y != 0 || width != m_width || height != m_height))
    358       CommitClear(cmdbuf);
    359     else
    360       m_state = State::Dirty;
    361   }
    362 
    363   // first time the texture is used? don't leave it undefined
    364   if (m_layout == Layout::Undefined)
    365     TransitionToLayout(cmdbuf, Layout::TransferDst);
    366 
    367   UpdateFromBuffer(cmdbuf, x, y, width, height, layer, level, upload_pitch, buffer, buffer_offset);
    368   TransitionToLayout(cmdbuf, Layout::ShaderReadOnly);
    369   return true;
    370 }
    371 
    372 bool VulkanTexture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer, u32 level)
    373 {
    374   // TODO: linear textures for dynamic?
    375   if ((x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || layer > m_layers || level > m_levels)
    376   {
    377     return false;
    378   }
    379 
    380   VulkanDevice& dev = VulkanDevice::GetInstance();
    381   if (m_state == GPUTexture::State::Cleared && (x != 0 || y != 0 || width != m_width || height != m_height))
    382     CommitClear(GetCommandBufferForUpdate());
    383 
    384   // see note in Update() for the reason why.
    385   const u32 aligned_pitch = Common::AlignUpPow2(width * GetPixelSize(), dev.GetBufferCopyRowPitchAlignment());
    386   const u32 req_size = height * aligned_pitch;
    387   VulkanStreamBuffer& buffer = dev.GetTextureUploadBuffer();
    388   if (req_size >= (buffer.GetCurrentSize() / 2))
    389     return false;
    390 
    391   if (!buffer.ReserveMemory(req_size, dev.GetBufferCopyOffsetAlignment())) [[unlikely]]
    392   {
    393     dev.SubmitCommandBuffer(false, TinyString::from_format("Needs {} bytes in texture upload buffer", req_size));
    394     if (!buffer.ReserveMemory(req_size, dev.GetBufferCopyOffsetAlignment())) [[unlikely]]
    395       Panic("Failed to reserve texture upload memory");
    396   }
    397 
    398   // map for writing
    399   *map = buffer.GetCurrentHostPointer();
    400   *map_stride = aligned_pitch;
    401   m_map_x = static_cast<u16>(x);
    402   m_map_y = static_cast<u16>(y);
    403   m_map_width = static_cast<u16>(width);
    404   m_map_height = static_cast<u16>(height);
    405   m_map_layer = static_cast<u8>(layer);
    406   m_map_level = static_cast<u8>(level);
    407   m_state = GPUTexture::State::Dirty;
    408   return true;
    409 }
    410 
    411 void VulkanTexture::Unmap()
    412 {
    413   VulkanDevice& dev = VulkanDevice::GetInstance();
    414   VulkanStreamBuffer& sb = dev.GetTextureUploadBuffer();
    415   const u32 aligned_pitch = Common::AlignUpPow2(m_map_width * GetPixelSize(), dev.GetBufferCopyRowPitchAlignment());
    416   const u32 req_size = m_map_height * aligned_pitch;
    417   const u32 offset = sb.GetCurrentOffset();
    418   sb.CommitMemory(req_size);
    419 
    420   GPUDevice::GetStatistics().buffer_streamed += req_size;
    421   GPUDevice::GetStatistics().num_uploads++;
    422 
    423   // first time the texture is used? don't leave it undefined
    424   const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
    425   if (m_layout == Layout::Undefined)
    426     TransitionToLayout(cmdbuf, Layout::TransferDst);
    427 
    428   UpdateFromBuffer(cmdbuf, m_map_x, m_map_y, m_map_width, m_map_height, m_map_layer, m_map_level, aligned_pitch,
    429                    sb.GetBuffer(), offset);
    430   TransitionToLayout(cmdbuf, Layout::ShaderReadOnly);
    431 
    432   m_map_x = 0;
    433   m_map_y = 0;
    434   m_map_width = 0;
    435   m_map_height = 0;
    436   m_map_layer = 0;
    437   m_map_level = 0;
    438 }
    439 
    440 void VulkanTexture::CommitClear()
    441 {
    442   if (m_state != GPUTexture::State::Cleared)
    443     return;
    444 
    445   VulkanDevice& dev = VulkanDevice::GetInstance();
    446   if (dev.InRenderPass())
    447     dev.EndRenderPass();
    448 
    449   CommitClear(dev.GetCurrentCommandBuffer());
    450 }
    451 
    452 void VulkanTexture::CommitClear(VkCommandBuffer cmdbuf)
    453 {
    454   TransitionToLayout(cmdbuf, Layout::ClearDst);
    455 
    456   if (IsDepthStencil())
    457   {
    458     const VkClearDepthStencilValue cv = {m_clear_value.depth, 0u};
    459     const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u};
    460     vkCmdClearDepthStencilImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
    461   }
    462   else
    463   {
    464     alignas(16) VkClearColorValue cv;
    465     std::memcpy(cv.float32, GetUNormClearColor().data(), sizeof(cv.float32));
    466     const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
    467     vkCmdClearColorImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
    468   }
    469 
    470   SetState(GPUTexture::State::Dirty);
    471 }
    472 
    473 void VulkanTexture::OverrideImageLayout(Layout new_layout)
    474 {
    475   m_layout = new_layout;
    476 }
    477 
    478 void VulkanTexture::SetDebugName(std::string_view name)
    479 {
    480   VulkanDevice& dev = VulkanDevice::GetInstance();
    481   Vulkan::SetObjectName(dev.GetVulkanDevice(), m_image, name);
    482   Vulkan::SetObjectName(dev.GetVulkanDevice(), m_view, name);
    483 }
    484 
    485 void VulkanTexture::TransitionToLayout(Layout layout)
    486 {
    487   TransitionToLayout(VulkanDevice::GetInstance().GetCurrentCommandBuffer(), layout);
    488 }
    489 
    490 void VulkanTexture::TransitionToLayout(VkCommandBuffer command_buffer, Layout new_layout)
    491 {
    492   // Need a barrier inbetween multiple self transfers.
    493   if (m_layout == new_layout && new_layout != Layout::TransferSelf)
    494     return;
    495 
    496   TransitionSubresourcesToLayout(command_buffer, 0, m_layers, 0, m_levels, m_layout, new_layout);
    497 
    498   m_layout = new_layout;
    499 }
    500 
    501 void VulkanTexture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_layer, u32 num_layers,
    502                                                    u32 start_level, u32 num_levels, Layout old_layout,
    503                                                    Layout new_layout)
    504 {
    505   TransitionSubresourcesToLayout(command_buffer, m_image, m_type, start_layer, num_layers, start_level, num_levels,
    506                                  old_layout, new_layout);
    507 }
    508 
    509 void VulkanTexture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, VkImage image, Type type,
    510                                                    u32 start_layer, u32 num_layers, u32 start_level, u32 num_levels,
    511                                                    Layout old_layout, Layout new_layout)
    512 {
    513   VkImageAspectFlags aspect;
    514   if (type == Type::DepthStencil)
    515   {
    516     // TODO: detect stencil
    517     aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
    518   }
    519   else
    520   {
    521     aspect = VK_IMAGE_ASPECT_COLOR_BIT;
    522   }
    523 
    524   VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
    525                                   nullptr,
    526                                   0,
    527                                   0,
    528                                   GetVkImageLayout(old_layout),
    529                                   GetVkImageLayout(new_layout),
    530                                   VK_QUEUE_FAMILY_IGNORED,
    531                                   VK_QUEUE_FAMILY_IGNORED,
    532                                   image,
    533                                   {aspect, start_level, num_levels, start_layer, num_layers}};
    534 
    535   // srcStageMask -> Stages that must complete before the barrier
    536   // dstStageMask -> Stages that must wait for after the barrier before beginning
    537   VkPipelineStageFlags srcStageMask, dstStageMask;
    538   switch (old_layout)
    539   {
    540     case Layout::Undefined:
    541       // Layout undefined therefore contents undefined, and we don't care what happens to it.
    542       barrier.srcAccessMask = 0;
    543       srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    544       break;
    545 
    546     case Layout::Preinitialized:
    547       // Image has been pre-initialized by the host, so ensure all writes have completed.
    548       barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
    549       srcStageMask = VK_PIPELINE_STAGE_HOST_BIT;
    550       break;
    551 
    552     case Layout::ColorAttachment:
    553       // Image was being used as a color attachment, so ensure all writes have completed.
    554       barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    555       srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    556       break;
    557 
    558     case Layout::DepthStencilAttachment:
    559       // Image was being used as a depthstencil attachment, so ensure all writes have completed.
    560       barrier.srcAccessMask =
    561         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    562       srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    563       break;
    564 
    565     case Layout::ShaderReadOnly:
    566       // Image was being used as a shader resource, make sure all reads have finished.
    567       barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
    568       srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    569       break;
    570 
    571     case Layout::ClearDst:
    572       // Image was being used as a clear destination, ensure all writes have finished.
    573       barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    574       srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    575       break;
    576 
    577     case Layout::TransferSrc:
    578       // Image was being used as a copy source, ensure all reads have finished.
    579       barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
    580       srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    581       break;
    582 
    583     case Layout::TransferDst:
    584       // Image was being used as a copy destination, ensure all writes have finished.
    585       barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    586       srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    587       break;
    588 
    589     case Layout::TransferSelf:
    590       // Image was being used as a copy source and destination, ensure all reads and writes have finished.
    591       barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
    592       srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    593       break;
    594 
    595     case Layout::FeedbackLoop:
    596       barrier.srcAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
    597                                 (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
    598                                  VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
    599                                 (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
    600                                  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
    601       srcStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
    602                        (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
    603                        (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
    604                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
    605       break;
    606 
    607     case Layout::ReadWriteImage:
    608       barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
    609       srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    610       break;
    611 
    612     case Layout::ComputeReadWriteImage:
    613       barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
    614       srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    615       break;
    616 
    617     case Layout::General:
    618     default:
    619       srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    620       break;
    621   }
    622 
    623   switch (new_layout)
    624   {
    625     case Layout::Undefined:
    626       barrier.dstAccessMask = 0;
    627       dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    628       break;
    629 
    630     case Layout::ColorAttachment:
    631       barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    632       dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    633       break;
    634 
    635     case Layout::DepthStencilAttachment:
    636       barrier.dstAccessMask =
    637         VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    638       dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    639       break;
    640 
    641     case Layout::ShaderReadOnly:
    642       barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    643       dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    644       break;
    645 
    646     case Layout::ClearDst:
    647       barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    648       dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    649       break;
    650 
    651     case Layout::TransferSrc:
    652       barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
    653       dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    654       break;
    655 
    656     case Layout::TransferDst:
    657       barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    658       dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    659       break;
    660 
    661     case Layout::TransferSelf:
    662       barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
    663       dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    664       break;
    665 
    666     case Layout::PresentSrc:
    667       srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
    668       dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    669       break;
    670 
    671     case Layout::FeedbackLoop:
    672       barrier.dstAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
    673                                 (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
    674                                  VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
    675                                 (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
    676                                  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
    677       dstStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
    678                        (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
    679                        (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
    680                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
    681       break;
    682 
    683     case Layout::ReadWriteImage:
    684       barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
    685       dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    686       break;
    687 
    688     case Layout::ComputeReadWriteImage:
    689       barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
    690       dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    691       break;
    692 
    693     case Layout::General:
    694     default:
    695       dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    696       break;
    697   }
    698   vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
    699 }
    700 
    701 VkDescriptorSet VulkanTexture::GetDescriptorSetWithSampler(VkSampler sampler)
    702 {
    703   for (const auto& it : m_descriptor_sets)
    704   {
    705     if (it.first == sampler)
    706       return it.second;
    707   }
    708 
    709   VulkanDevice& dev = VulkanDevice::GetInstance();
    710   VkDescriptorSet ds = dev.AllocatePersistentDescriptorSet(dev.m_single_texture_ds_layout);
    711   if (ds == VK_NULL_HANDLE)
    712     Panic("Failed to allocate persistent descriptor set.");
    713 
    714   Vulkan::DescriptorSetUpdateBuilder dsub;
    715   dsub.AddCombinedImageSamplerDescriptorWrite(ds, 0, m_view, sampler);
    716   dsub.Update(dev.GetVulkanDevice(), false);
    717   m_descriptor_sets.emplace_back(sampler, ds);
    718   return ds;
    719 }
    720 
    721 void VulkanTexture::MakeReadyForSampling()
    722 {
    723   if (m_layout == Layout::ShaderReadOnly)
    724     return;
    725 
    726   VulkanDevice& dev = VulkanDevice::GetInstance();
    727   if (dev.InRenderPass())
    728     dev.EndRenderPass();
    729 
    730   TransitionToLayout(Layout::ShaderReadOnly);
    731 }
    732 
    733 std::unique_ptr<GPUTexture> VulkanDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
    734                                                         GPUTexture::Type type, GPUTexture::Format format,
    735                                                         const void* data /* = nullptr */, u32 data_stride /* = 0 */)
    736 {
    737   const VkFormat vk_format = VulkanDevice::TEXTURE_FORMAT_MAPPING[static_cast<u8>(format)];
    738   std::unique_ptr<VulkanTexture> tex =
    739     VulkanTexture::Create(width, height, layers, levels, samples, type, format, vk_format);
    740   if (tex && data)
    741     tex->Update(0, 0, width, height, data, data_stride);
    742 
    743   return tex;
    744 }
    745 
    746 VulkanSampler::VulkanSampler(VkSampler sampler) : m_sampler(sampler)
    747 {
    748 }
    749 
    750 VulkanSampler::~VulkanSampler()
    751 {
    752   // Cleaned up by main class.
    753 }
    754 
    755 void VulkanSampler::SetDebugName(std::string_view name)
    756 {
    757   Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_sampler, name);
    758 }
    759 
    760 VkSampler VulkanDevice::GetSampler(const GPUSampler::Config& config)
    761 {
    762   const auto it = m_sampler_map.find(config.key);
    763   if (it != m_sampler_map.end())
    764     return it->second;
    765 
    766   static constexpr std::array<VkSamplerAddressMode, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{
    767     VK_SAMPLER_ADDRESS_MODE_REPEAT,          // Repeat
    768     VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,   // ClampToEdge
    769     VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, // ClampToBorder
    770     VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, // MirrorRepeat
    771   }};
    772   static constexpr std::array<VkFilter, static_cast<u8>(GPUSampler::Filter::MaxCount)> min_mag_filters = {{
    773     VK_FILTER_NEAREST, // Nearest
    774     VK_FILTER_LINEAR,  // Linear
    775   }};
    776   static constexpr std::array<VkSamplerMipmapMode, static_cast<u8>(GPUSampler::Filter::MaxCount)> mip_filters = {{
    777     VK_SAMPLER_MIPMAP_MODE_NEAREST, // Nearest
    778     VK_SAMPLER_MIPMAP_MODE_LINEAR,  // Linear
    779   }};
    780   struct BorderColorMapping
    781   {
    782     u32 color;
    783     VkBorderColor vk_color;
    784   };
    785   static constexpr BorderColorMapping border_color_mapping[] = {
    786     {0x00000000u, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK},
    787     {0xFF000000u, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK},
    788     {0xFFFFFFFFu, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE},
    789   };
    790 
    791   // See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSamplerCreateInfo.html#_description
    792   // for the reasoning behind 0.25f here.
    793   VkSamplerCreateInfo ci = {
    794     VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
    795     nullptr,
    796     0,
    797     min_mag_filters[static_cast<u8>(config.min_filter.GetValue())],     // min
    798     min_mag_filters[static_cast<u8>(config.mag_filter.GetValue())],     // mag
    799     mip_filters[static_cast<u8>(config.mip_filter.GetValue())],         // mip
    800     ta[static_cast<u8>(config.address_u.GetValue())],                   // u
    801     ta[static_cast<u8>(config.address_v.GetValue())],                   // v
    802     ta[static_cast<u8>(config.address_w.GetValue())],                   // w
    803     0.0f,                                                               // lod bias
    804     static_cast<VkBool32>(config.anisotropy > 1),                       // anisotropy enable
    805     static_cast<float>(config.anisotropy),                              // anisotropy
    806     VK_FALSE,                                                           // compare enable
    807     VK_COMPARE_OP_ALWAYS,                                               // compare op
    808     static_cast<float>(config.min_lod),                                 // min lod
    809     (config.max_lod == 0) ? 0.25f : static_cast<float>(config.max_lod), // max lod
    810     VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,                            // border
    811     VK_FALSE                                                            // unnormalized coordinates
    812   };
    813 
    814   if (config.address_u == GPUSampler::AddressMode::ClampToBorder ||
    815       config.address_v == GPUSampler::AddressMode::ClampToBorder ||
    816       config.address_w == GPUSampler::AddressMode::ClampToBorder)
    817   {
    818     u32 i;
    819     for (i = 0; i < static_cast<u32>(std::size(border_color_mapping)); i++)
    820     {
    821       if (border_color_mapping[i].color == config.border_color)
    822         break;
    823     }
    824     if (i == std::size(border_color_mapping))
    825     {
    826       ERROR_LOG("Unsupported border color: {:08X}", config.border_color.GetValue());
    827       return {};
    828     }
    829 
    830     ci.borderColor = border_color_mapping[i].vk_color;
    831   }
    832 
    833   VkSampler sampler = VK_NULL_HANDLE;
    834   VkResult res = vkCreateSampler(m_device, &ci, nullptr, &sampler);
    835   if (res != VK_SUCCESS)
    836     LOG_VULKAN_ERROR(res, "vkCreateSampler() failed: ");
    837 
    838   m_sampler_map.emplace(config.key, sampler);
    839   return sampler;
    840 }
    841 
    842 void VulkanDevice::DestroySamplers()
    843 {
    844   for (auto& it : m_sampler_map)
    845   {
    846     if (it.second != VK_NULL_HANDLE)
    847       vkDestroySampler(m_device, it.second, nullptr);
    848   }
    849   m_sampler_map.clear();
    850 }
    851 
    852 std::unique_ptr<GPUSampler> VulkanDevice::CreateSampler(const GPUSampler::Config& config)
    853 {
    854   const VkSampler vsampler = GetSampler(config);
    855   if (vsampler == VK_NULL_HANDLE)
    856     return {};
    857 
    858   return std::unique_ptr<GPUSampler>(new VulkanSampler(vsampler));
    859 }
    860 
    861 VulkanTextureBuffer::VulkanTextureBuffer(Format format, u32 size_in_elements)
    862   : GPUTextureBuffer(format, size_in_elements)
    863 {
    864 }
    865 
    866 VulkanTextureBuffer::~VulkanTextureBuffer()
    867 {
    868   Destroy(true);
    869 }
    870 
    871 bool VulkanTextureBuffer::CreateBuffer(bool ssbo)
    872 {
    873   return m_buffer.Create(ssbo ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
    874                          GetSizeInBytes());
    875 }
    876 
    877 void VulkanTextureBuffer::Destroy(bool defer)
    878 {
    879   VulkanDevice& dev = VulkanDevice::GetInstance();
    880   if (m_buffer_view != VK_NULL_HANDLE)
    881   {
    882     if (defer)
    883       dev.DeferBufferViewDestruction(m_buffer_view);
    884     else
    885       vkDestroyBufferView(dev.GetVulkanDevice(), m_buffer_view, nullptr);
    886   }
    887   if (m_descriptor_set != VK_NULL_HANDLE)
    888   {
    889     if (defer)
    890       dev.DeferPersistentDescriptorSetDestruction(m_descriptor_set);
    891     else
    892       dev.FreePersistentDescriptorSet(m_descriptor_set);
    893   }
    894 }
    895 
    896 void* VulkanTextureBuffer::Map(u32 required_elements)
    897 {
    898   const u32 esize = GetElementSize(m_format);
    899   const u32 req_size = esize * required_elements;
    900   if (!m_buffer.ReserveMemory(req_size, esize))
    901   {
    902     VulkanDevice::GetInstance().SubmitCommandBufferAndRestartRenderPass("out of space in texture buffer");
    903     if (!m_buffer.ReserveMemory(req_size, esize))
    904       Panic("Failed to allocate texture buffer space.");
    905   }
    906 
    907   m_current_position = m_buffer.GetCurrentOffset() / esize;
    908   return m_buffer.GetCurrentHostPointer();
    909 }
    910 
    911 void VulkanTextureBuffer::Unmap(u32 used_elements)
    912 {
    913   const u32 size = GetElementSize(m_format) * used_elements;
    914   GPUDevice::GetStatistics().buffer_streamed += size;
    915   GPUDevice::GetStatistics().num_uploads++;
    916   m_buffer.CommitMemory(size);
    917 }
    918 
    919 void VulkanTextureBuffer::SetDebugName(std::string_view name)
    920 {
    921   VulkanDevice& dev = VulkanDevice::GetInstance();
    922   Vulkan::SetObjectName(dev.GetVulkanDevice(), m_buffer.GetBuffer(), name);
    923   if (m_buffer_view != VK_NULL_HANDLE)
    924     Vulkan::SetObjectName(dev.GetVulkanDevice(), m_buffer_view, name);
    925 }
    926 
    927 std::unique_ptr<GPUTextureBuffer> VulkanDevice::CreateTextureBuffer(GPUTextureBuffer::Format format,
    928                                                                     u32 size_in_elements)
    929 {
    930   static constexpr std::array<VkFormat, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{
    931     VK_FORMAT_R16_UINT, // R16UI
    932   }};
    933 
    934   const bool ssbo = m_features.texture_buffers_emulated_with_ssbo;
    935   std::unique_ptr<VulkanTextureBuffer> tb = std::make_unique<VulkanTextureBuffer>(format, size_in_elements);
    936   if (!tb->CreateBuffer(ssbo))
    937     return {};
    938 
    939   tb->m_descriptor_set = AllocatePersistentDescriptorSet(m_single_texture_buffer_ds_layout);
    940   if (tb->m_descriptor_set == VK_NULL_HANDLE)
    941   {
    942     ERROR_LOG("Failed to allocate persistent descriptor set for texture buffer.");
    943     tb->Destroy(false);
    944     return {};
    945   }
    946 
    947   Vulkan::DescriptorSetUpdateBuilder dsub;
    948   if (ssbo)
    949   {
    950     dsub.AddBufferDescriptorWrite(tb->m_descriptor_set, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, tb->GetBuffer(), 0,
    951                                   tb->GetSizeInBytes());
    952   }
    953   else
    954   {
    955     Vulkan::BufferViewBuilder bvb;
    956     bvb.Set(tb->GetBuffer(), format_mapping[static_cast<u8>(format)], 0, tb->GetSizeInBytes());
    957     if ((tb->m_buffer_view = bvb.Create(m_device, false)) == VK_NULL_HANDLE)
    958     {
    959       ERROR_LOG("Failed to create buffer view for texture buffer.");
    960       tb->Destroy(false);
    961       return {};
    962     }
    963 
    964     dsub.AddBufferViewDescriptorWrite(tb->m_descriptor_set, 0, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
    965                                       tb->m_buffer_view);
    966   }
    967   dsub.Update(m_device, false);
    968 
    969   return tb;
    970 }
    971 
    972 VulkanDownloadTexture::VulkanDownloadTexture(u32 width, u32 height, GPUTexture::Format format, VmaAllocation allocation,
    973                                              VkDeviceMemory memory, VkBuffer buffer, VkDeviceSize memory_offset,
    974                                              VkDeviceSize buffer_size, const u8* map_ptr, u32 map_pitch)
    975   : GPUDownloadTexture(width, height, format, (memory != VK_NULL_HANDLE)), m_allocation(allocation), m_memory(memory),
    976     m_buffer(buffer), m_memory_offset(memory_offset), m_buffer_size(buffer_size)
    977 {
    978   m_map_pointer = map_ptr;
    979   m_current_pitch = map_pitch;
    980 }
    981 
    982 VulkanDownloadTexture::~VulkanDownloadTexture()
    983 {
    984   if (m_allocation != VK_NULL_HANDLE)
    985   {
    986     // Buffer was created mapped, no need to manually unmap.
    987     VulkanDevice::GetInstance().DeferBufferDestruction(m_buffer, m_allocation);
    988   }
    989   else
    990   {
    991     // imported
    992     DebugAssert(m_is_imported && m_memory != VK_NULL_HANDLE);
    993     VulkanDevice::GetInstance().DeferBufferDestruction(m_buffer, m_memory);
    994   }
    995 }
    996 
    997 std::unique_ptr<VulkanDownloadTexture> VulkanDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format,
    998                                                                      void* memory, size_t memory_size,
    999                                                                      u32 memory_stride)
   1000 {
   1001   VulkanDevice& dev = VulkanDevice::GetInstance();
   1002   VmaAllocation allocation = VK_NULL_HANDLE;
   1003   VkDeviceMemory dev_memory = VK_NULL_HANDLE;
   1004   VkBuffer buffer = VK_NULL_HANDLE;
   1005   VkDeviceSize memory_offset = 0;
   1006   const u8* map_ptr = nullptr;
   1007   u32 map_pitch = 0;
   1008   u32 buffer_size = 0;
   1009 
   1010   // not importing memory?
   1011   if (!memory)
   1012   {
   1013     map_pitch = Common::AlignUpPow2(GPUTexture::CalcUploadPitch(format, width), dev.GetBufferCopyRowPitchAlignment());
   1014     buffer_size = height * map_pitch;
   1015 
   1016     const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
   1017                                     nullptr,
   1018                                     0u,
   1019                                     buffer_size,
   1020                                     VK_BUFFER_USAGE_TRANSFER_DST_BIT,
   1021                                     VK_SHARING_MODE_EXCLUSIVE,
   1022                                     0u,
   1023                                     nullptr};
   1024 
   1025     VmaAllocationCreateInfo aci = {};
   1026     aci.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
   1027     aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
   1028     aci.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
   1029 
   1030     VmaAllocationInfo ai = {};
   1031     VkResult res = vmaCreateBuffer(VulkanDevice::GetInstance().GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
   1032     if (res != VK_SUCCESS)
   1033     {
   1034       LOG_VULKAN_ERROR(res, "vmaCreateBuffer() failed: ");
   1035       return {};
   1036     }
   1037 
   1038     DebugAssert(ai.pMappedData);
   1039     map_ptr = static_cast<u8*>(ai.pMappedData);
   1040   }
   1041   else
   1042   {
   1043     map_pitch = memory_stride;
   1044     buffer_size = height * map_pitch;
   1045     Assert(buffer_size <= memory_size);
   1046 
   1047     if (!dev.TryImportHostMemory(memory, memory_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, &dev_memory, &buffer,
   1048                                  &memory_offset))
   1049     {
   1050       return {};
   1051     }
   1052 
   1053     map_ptr = static_cast<u8*>(memory);
   1054   }
   1055 
   1056   return std::unique_ptr<VulkanDownloadTexture>(new VulkanDownloadTexture(
   1057     width, height, format, allocation, dev_memory, buffer, memory_offset, buffer_size, map_ptr, map_pitch));
   1058 }
   1059 
   1060 void VulkanDownloadTexture::CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width,
   1061                                             u32 height, u32 src_layer, u32 src_level, bool use_transfer_pitch)
   1062 {
   1063   VulkanTexture* const vkTex = static_cast<VulkanTexture*>(src);
   1064   VulkanDevice& dev = VulkanDevice::GetInstance();
   1065 
   1066   DebugAssert(vkTex->GetFormat() == m_format);
   1067   DebugAssert(src_level < vkTex->GetLevels());
   1068   DebugAssert((src_x + width) <= src->GetMipWidth(src_level) && (src_y + height) <= src->GetMipHeight(src_level));
   1069   DebugAssert((dst_x + width) <= m_width && (dst_y + height) <= m_height);
   1070   DebugAssert((dst_x == 0 && dst_y == 0) || !use_transfer_pitch);
   1071   DebugAssert(!m_is_imported || !use_transfer_pitch);
   1072 
   1073   u32 copy_offset, copy_size, copy_rows;
   1074   if (!m_is_imported)
   1075     m_current_pitch = GetTransferPitch(use_transfer_pitch ? width : m_width, dev.GetBufferCopyRowPitchAlignment());
   1076   GetTransferSize(dst_x, dst_y, width, height, m_current_pitch, &copy_offset, &copy_size, &copy_rows);
   1077 
   1078   dev.GetStatistics().num_downloads++;
   1079   if (dev.InRenderPass())
   1080     dev.EndRenderPass();
   1081   vkTex->CommitClear();
   1082 
   1083   const VkCommandBuffer cmdbuf = dev.GetCurrentCommandBuffer();
   1084   GL_INS_FMT("VulkanDownloadTexture::CopyFromTexture: {{{},{}}} {}x{} => {{{},{}}}", src_x, src_y, width, height, dst_x,
   1085              dst_y);
   1086 
   1087   VulkanTexture::Layout old_layout = vkTex->GetLayout();
   1088   if (old_layout == VulkanTexture::Layout::Undefined)
   1089     vkTex->TransitionToLayout(cmdbuf, VulkanTexture::Layout::TransferSrc);
   1090   else if (old_layout != VulkanTexture::Layout::TransferSrc)
   1091     vkTex->TransitionSubresourcesToLayout(cmdbuf, 0, 1, src_level, 1, old_layout, VulkanTexture::Layout::TransferSrc);
   1092 
   1093   VkBufferImageCopy image_copy = {};
   1094   const VkImageAspectFlags aspect = vkTex->IsDepthStencil() ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
   1095   image_copy.bufferOffset = m_memory_offset + copy_offset;
   1096   image_copy.bufferRowLength = GPUTexture::CalcUploadRowLengthFromPitch(m_format, m_current_pitch);
   1097   image_copy.bufferImageHeight = 0;
   1098   image_copy.imageSubresource = {aspect, src_level, src_layer, 1u};
   1099   image_copy.imageOffset = {static_cast<s32>(src_x), static_cast<s32>(src_y), 0};
   1100   image_copy.imageExtent = {width, height, 1u};
   1101 
   1102   // do the copy
   1103   vkCmdCopyImageToBuffer(cmdbuf, vkTex->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, &image_copy);
   1104 
   1105   // flush gpu cache
   1106   const VkBufferMemoryBarrier buffer_info = {
   1107     VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType    sType
   1108     nullptr,                                 // const void*        pNext
   1109     VK_ACCESS_TRANSFER_WRITE_BIT,            // VkAccessFlags      srcAccessMask
   1110     VK_ACCESS_HOST_READ_BIT,                 // VkAccessFlags      dstAccessMask
   1111     VK_QUEUE_FAMILY_IGNORED,                 // uint32_t           srcQueueFamilyIndex
   1112     VK_QUEUE_FAMILY_IGNORED,                 // uint32_t           dstQueueFamilyIndex
   1113     m_buffer,                                // VkBuffer           buffer
   1114     0,                                       // VkDeviceSize       offset
   1115     copy_size                                // VkDeviceSize       size
   1116   };
   1117   vkCmdPipelineBarrier(cmdbuf, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 1, &buffer_info,
   1118                        0, nullptr);
   1119 
   1120   if (old_layout != VulkanTexture::Layout::TransferSrc && old_layout != VulkanTexture::Layout::Undefined)
   1121     vkTex->TransitionSubresourcesToLayout(cmdbuf, 0, 1, src_level, 1, VulkanTexture::Layout::TransferSrc, old_layout);
   1122 
   1123   m_copy_fence_counter = dev.GetCurrentFenceCounter();
   1124   m_needs_cache_invalidate = true;
   1125   m_needs_flush = true;
   1126 }
   1127 
   1128 bool VulkanDownloadTexture::Map(u32 x, u32 y, u32 width, u32 height)
   1129 {
   1130   // Always mapped, but we might need to invalidate the cache.
   1131   if (m_needs_cache_invalidate)
   1132   {
   1133     u32 copy_offset, copy_size, copy_rows;
   1134     GetTransferSize(x, y, width, height, m_current_pitch, &copy_offset, &copy_size, &copy_rows);
   1135     vmaInvalidateAllocation(VulkanDevice::GetInstance().GetAllocator(), m_allocation, copy_offset,
   1136                             m_current_pitch * copy_rows);
   1137     m_needs_cache_invalidate = false;
   1138   }
   1139 
   1140   return true;
   1141 }
   1142 
   1143 void VulkanDownloadTexture::Unmap()
   1144 {
   1145   // Always mapped.
   1146 }
   1147 
   1148 void VulkanDownloadTexture::Flush()
   1149 {
   1150   if (!m_needs_flush)
   1151     return;
   1152 
   1153   m_needs_flush = false;
   1154 
   1155   VulkanDevice& dev = VulkanDevice::GetInstance();
   1156   if (dev.GetCompletedFenceCounter() >= m_copy_fence_counter)
   1157     return;
   1158 
   1159   // Need to execute command buffer.
   1160   if (dev.GetCurrentFenceCounter() == m_copy_fence_counter)
   1161   {
   1162     if (dev.InRenderPass())
   1163       dev.EndRenderPass();
   1164     dev.SubmitCommandBuffer(true);
   1165   }
   1166   else
   1167   {
   1168     dev.WaitForFenceCounter(m_copy_fence_counter);
   1169   }
   1170 }
   1171 
   1172 void VulkanDownloadTexture::SetDebugName(std::string_view name)
   1173 {
   1174   if (name.empty())
   1175     return;
   1176 
   1177   Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_buffer, name);
   1178 }
   1179 
   1180 std::unique_ptr<GPUDownloadTexture> VulkanDevice::CreateDownloadTexture(u32 width, u32 height,
   1181                                                                         GPUTexture::Format format)
   1182 {
   1183   return VulkanDownloadTexture::Create(width, height, format, nullptr, 0, 0);
   1184 }
   1185 
   1186 std::unique_ptr<GPUDownloadTexture> VulkanDevice::CreateDownloadTexture(u32 width, u32 height,
   1187                                                                         GPUTexture::Format format, void* memory,
   1188                                                                         size_t memory_size, u32 memory_stride)
   1189 {
   1190   return VulkanDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride);
   1191 }