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, ©_offset, ©_size, ©_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, ©_offset, ©_size, ©_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 }