vk_texture_runtime.cpp
1 // Copyright 2023 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include <boost/container/small_vector.hpp> 6 #include <boost/container/static_vector.hpp> 7 8 #include "common/literals.h" 9 #include "common/microprofile.h" 10 #include "common/scope_exit.h" 11 #include "video_core/custom_textures/material.h" 12 #include "video_core/rasterizer_cache/texture_codec.h" 13 #include "video_core/rasterizer_cache/utils.h" 14 #include "video_core/renderer_vulkan/pica_to_vk.h" 15 #include "video_core/renderer_vulkan/vk_descriptor_pool.h" 16 #include "video_core/renderer_vulkan/vk_instance.h" 17 #include "video_core/renderer_vulkan/vk_renderpass_cache.h" 18 #include "video_core/renderer_vulkan/vk_scheduler.h" 19 #include "video_core/renderer_vulkan/vk_texture_runtime.h" 20 21 #include <vk_mem_alloc.h> 22 #include <vulkan/vulkan_format_traits.hpp> 23 24 // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. 25 #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) 26 #pragma GCC diagnostic push 27 #pragma GCC diagnostic ignored "-Wclass-memaccess" 28 #endif 29 30 MICROPROFILE_DEFINE(Vulkan_ImageAlloc, "Vulkan", "Texture Allocation", MP_RGB(192, 52, 235)); 31 32 namespace Vulkan { 33 34 namespace { 35 36 using VideoCore::MapType; 37 using VideoCore::PixelFormat; 38 using VideoCore::SurfaceType; 39 using VideoCore::TextureType; 40 using namespace Common::Literals; 41 42 struct RecordParams { 43 vk::ImageAspectFlags aspect; 44 vk::Filter filter; 45 vk::PipelineStageFlags pipeline_flags; 46 vk::AccessFlags src_access; 47 vk::AccessFlags dst_access; 48 vk::Image src_image; 49 vk::Image dst_image; 50 }; 51 52 vk::Filter MakeFilter(VideoCore::PixelFormat pixel_format) { 53 switch (pixel_format) { 54 case VideoCore::PixelFormat::D16: 55 case VideoCore::PixelFormat::D24: 56 case VideoCore::PixelFormat::D24S8: 57 return vk::Filter::eNearest; 58 default: 59 return vk::Filter::eLinear; 60 } 61 } 62 63 [[nodiscard]] vk::ClearValue MakeClearValue(VideoCore::ClearValue clear) { 64 static_assert(sizeof(VideoCore::ClearValue) == sizeof(vk::ClearValue)); 65 66 vk::ClearValue value{}; 67 std::memcpy(&value, &clear, sizeof(vk::ClearValue)); 68 return value; 69 } 70 71 [[nodiscard]] vk::ClearColorValue MakeClearColorValue(Common::Vec4f color) { 72 return vk::ClearColorValue{ 73 .float32 = std::array{color[0], color[1], color[2], color[3]}, 74 }; 75 } 76 77 [[nodiscard]] vk::ClearDepthStencilValue MakeClearDepthStencilValue(VideoCore::ClearValue clear) { 78 return vk::ClearDepthStencilValue{ 79 .depth = clear.depth, 80 .stencil = clear.stencil, 81 }; 82 } 83 84 u32 UnpackDepthStencil(const VideoCore::StagingData& data, vk::Format dest) { 85 u32 depth_offset = 0; 86 u32 stencil_offset = 4 * data.size / 5; 87 const auto& mapped = data.mapped; 88 89 switch (dest) { 90 case vk::Format::eD24UnormS8Uint: { 91 for (; stencil_offset < data.size; depth_offset += 4) { 92 u8* ptr = mapped.data() + depth_offset; 93 const u32 d24s8 = VideoCore::MakeInt<u32>(ptr); 94 const u32 d24 = d24s8 >> 8; 95 mapped[stencil_offset] = d24s8 & 0xFF; 96 std::memcpy(ptr, &d24, 4); 97 stencil_offset++; 98 } 99 break; 100 } 101 case vk::Format::eD32SfloatS8Uint: { 102 for (; stencil_offset < data.size; depth_offset += 4) { 103 u8* ptr = mapped.data() + depth_offset; 104 const u32 d24s8 = VideoCore::MakeInt<u32>(ptr); 105 const float d32 = (d24s8 >> 8) / 16777215.f; 106 mapped[stencil_offset] = d24s8 & 0xFF; 107 std::memcpy(ptr, &d32, 4); 108 stencil_offset++; 109 } 110 break; 111 } 112 default: 113 LOG_ERROR(Render_Vulkan, "Unimplemented convertion for depth format {}", 114 vk::to_string(dest)); 115 UNREACHABLE(); 116 } 117 118 ASSERT(depth_offset == 4 * data.size / 5); 119 return depth_offset; 120 } 121 122 boost::container::small_vector<vk::ImageMemoryBarrier, 3> MakeInitBarriers( 123 vk::ImageAspectFlags aspect, std::span<const vk::Image> images) { 124 boost::container::small_vector<vk::ImageMemoryBarrier, 3> barriers; 125 for (const vk::Image& image : images) { 126 barriers.push_back(vk::ImageMemoryBarrier{ 127 .srcAccessMask = vk::AccessFlagBits::eNone, 128 .dstAccessMask = vk::AccessFlagBits::eNone, 129 .oldLayout = vk::ImageLayout::eUndefined, 130 .newLayout = vk::ImageLayout::eGeneral, 131 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 132 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 133 .image = image, 134 .subresourceRange{ 135 .aspectMask = aspect, 136 .baseMipLevel = 0, 137 .levelCount = VK_REMAINING_MIP_LEVELS, 138 .baseArrayLayer = 0, 139 .layerCount = VK_REMAINING_ARRAY_LAYERS, 140 }, 141 }); 142 } 143 return barriers; 144 } 145 146 Handle MakeHandle(const Instance* instance, u32 width, u32 height, u32 levels, TextureType type, 147 vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags flags, 148 vk::ImageAspectFlags aspect, bool need_format_list, 149 std::string_view debug_name = {}) { 150 const u32 layers = type == TextureType::CubeMap ? 6 : 1; 151 152 const std::array format_list = { 153 vk::Format::eR8G8B8A8Unorm, 154 vk::Format::eR32Uint, 155 }; 156 const vk::ImageFormatListCreateInfo image_format_list = { 157 .viewFormatCount = static_cast<u32>(format_list.size()), 158 .pViewFormats = format_list.data(), 159 }; 160 161 const vk::ImageCreateInfo image_info = { 162 .pNext = need_format_list ? &image_format_list : nullptr, 163 .flags = flags, 164 .imageType = vk::ImageType::e2D, 165 .format = format, 166 .extent = {width, height, 1}, 167 .mipLevels = levels, 168 .arrayLayers = layers, 169 .samples = vk::SampleCountFlagBits::e1, 170 .usage = usage, 171 }; 172 173 const VmaAllocationCreateInfo alloc_info = { 174 .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, 175 .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 176 .requiredFlags = 0, 177 .preferredFlags = 0, 178 .pool = VK_NULL_HANDLE, 179 .pUserData = nullptr, 180 }; 181 182 VkImage unsafe_image{}; 183 VkImageCreateInfo unsafe_image_info = static_cast<VkImageCreateInfo>(image_info); 184 VmaAllocation allocation{}; 185 186 VkResult result = vmaCreateImage(instance->GetAllocator(), &unsafe_image_info, &alloc_info, 187 &unsafe_image, &allocation, nullptr); 188 if (result != VK_SUCCESS) [[unlikely]] { 189 LOG_CRITICAL(Render_Vulkan, "Failed allocating image with error {}", result); 190 UNREACHABLE(); 191 } 192 193 const vk::Image image{unsafe_image}; 194 const vk::ImageViewCreateInfo view_info = { 195 .image = image, 196 .viewType = 197 type == TextureType::CubeMap ? vk::ImageViewType::eCube : vk::ImageViewType::e2D, 198 .format = format, 199 .subresourceRange{ 200 .aspectMask = aspect, 201 .baseMipLevel = 0, 202 .levelCount = levels, 203 .baseArrayLayer = 0, 204 .layerCount = layers, 205 }, 206 }; 207 vk::UniqueImageView image_view = instance->GetDevice().createImageViewUnique(view_info); 208 209 if (!debug_name.empty() && instance->HasDebuggingToolAttached()) { 210 Vulkan::SetObjectName(instance->GetDevice(), image, debug_name); 211 Vulkan::SetObjectName(instance->GetDevice(), image_view.get(), "{} View({})", debug_name, 212 vk::to_string(aspect)); 213 } 214 215 return Handle{ 216 .alloc = allocation, 217 .image = image, 218 .image_view = std::move(image_view), 219 }; 220 } 221 222 vk::UniqueFramebuffer MakeFramebuffer(vk::Device device, vk::RenderPass render_pass, u32 width, 223 u32 height, std::span<const vk::ImageView> attachments) { 224 const vk::FramebufferCreateInfo framebuffer_info = { 225 .renderPass = render_pass, 226 .attachmentCount = static_cast<u32>(attachments.size()), 227 .pAttachments = attachments.data(), 228 .width = width, 229 .height = height, 230 .layers = 1, 231 }; 232 return device.createFramebufferUnique(framebuffer_info); 233 } 234 235 vk::ImageSubresourceRange MakeSubresourceRange(vk::ImageAspectFlags aspect, u32 level = 0, 236 u32 levels = 1, u32 layer = 0) { 237 return vk::ImageSubresourceRange{ 238 .aspectMask = aspect, 239 .baseMipLevel = level, 240 .levelCount = levels, 241 .baseArrayLayer = layer, 242 .layerCount = VK_REMAINING_ARRAY_LAYERS, 243 }; 244 } 245 246 constexpr u64 UPLOAD_BUFFER_SIZE = 512_MiB; 247 constexpr u64 DOWNLOAD_BUFFER_SIZE = 16_MiB; 248 249 } // Anonymous namespace 250 251 TextureRuntime::TextureRuntime(const Instance& instance, Scheduler& scheduler, 252 RenderpassCache& renderpass_cache, DescriptorPool& pool, 253 DescriptorSetProvider& texture_provider_, u32 num_swapchain_images_) 254 : instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache}, 255 texture_provider{texture_provider_}, blit_helper{instance, scheduler, pool, renderpass_cache}, 256 upload_buffer{instance, scheduler, vk::BufferUsageFlagBits::eTransferSrc, UPLOAD_BUFFER_SIZE, 257 BufferType::Upload}, 258 download_buffer{instance, scheduler, 259 vk::BufferUsageFlagBits::eTransferDst | 260 vk::BufferUsageFlagBits::eStorageBuffer, 261 DOWNLOAD_BUFFER_SIZE, BufferType::Download}, 262 num_swapchain_images{num_swapchain_images_} {} 263 264 TextureRuntime::~TextureRuntime() = default; 265 266 VideoCore::StagingData TextureRuntime::FindStaging(u32 size, bool upload) { 267 StreamBuffer& buffer = upload ? upload_buffer : download_buffer; 268 const auto [data, offset, invalidate] = buffer.Map(size, 16); 269 return VideoCore::StagingData{ 270 .size = size, 271 .offset = static_cast<u32>(offset), 272 .mapped = std::span{data, size}, 273 }; 274 } 275 276 u32 TextureRuntime::RemoveThreshold() { 277 return num_swapchain_images; 278 } 279 280 void TextureRuntime::Finish() { 281 scheduler.Finish(); 282 } 283 284 bool TextureRuntime::Reinterpret(Surface& source, Surface& dest, 285 const VideoCore::TextureCopy& copy) { 286 const PixelFormat src_format = source.pixel_format; 287 const PixelFormat dst_format = dest.pixel_format; 288 ASSERT_MSG(src_format != dst_format, "Reinterpretation with the same format is invalid"); 289 290 if (!source.traits.needs_conversion && !dest.traits.needs_conversion && 291 source.type == dest.type) { 292 CopyTextures(source, dest, copy); 293 return true; 294 } 295 296 if (src_format == PixelFormat::D24S8 && dst_format == PixelFormat::RGBA8) { 297 blit_helper.ConvertDS24S8ToRGBA8(source, dest, copy); 298 } else { 299 LOG_WARNING(Render_Vulkan, "Unimplemented reinterpretation {} -> {}", 300 VideoCore::PixelFormatAsString(src_format), 301 VideoCore::PixelFormatAsString(dst_format)); 302 return false; 303 } 304 return true; 305 } 306 307 bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) { 308 renderpass_cache.EndRendering(); 309 310 const RecordParams params = { 311 .aspect = surface.Aspect(), 312 .pipeline_flags = surface.PipelineStageFlags(), 313 .src_access = surface.AccessFlags(), 314 .src_image = surface.Image(), 315 }; 316 317 if (clear.texture_rect == surface.GetScaledRect()) { 318 scheduler.Record([params, clear](vk::CommandBuffer cmdbuf) { 319 const vk::ImageSubresourceRange range = { 320 .aspectMask = params.aspect, 321 .baseMipLevel = clear.texture_level, 322 .levelCount = 1, 323 .baseArrayLayer = 0, 324 .layerCount = VK_REMAINING_ARRAY_LAYERS, 325 }; 326 327 const vk::ImageMemoryBarrier pre_barrier = { 328 .srcAccessMask = params.src_access, 329 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 330 .oldLayout = vk::ImageLayout::eGeneral, 331 .newLayout = vk::ImageLayout::eTransferDstOptimal, 332 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 333 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 334 .image = params.src_image, 335 .subresourceRange = range, 336 }; 337 338 const vk::ImageMemoryBarrier post_barrier = { 339 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 340 .dstAccessMask = params.src_access, 341 .oldLayout = vk::ImageLayout::eTransferDstOptimal, 342 .newLayout = vk::ImageLayout::eGeneral, 343 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 344 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 345 .image = params.src_image, 346 .subresourceRange = range, 347 }; 348 349 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 350 vk::DependencyFlagBits::eByRegion, {}, {}, pre_barrier); 351 352 const bool is_color = 353 static_cast<bool>(params.aspect & vk::ImageAspectFlagBits::eColor); 354 if (is_color) { 355 cmdbuf.clearColorImage(params.src_image, vk::ImageLayout::eTransferDstOptimal, 356 MakeClearColorValue(clear.value.color), range); 357 } else { 358 cmdbuf.clearDepthStencilImage(params.src_image, 359 vk::ImageLayout::eTransferDstOptimal, 360 MakeClearDepthStencilValue(clear.value), range); 361 } 362 363 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 364 vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier); 365 }); 366 return true; 367 } 368 369 ClearTextureWithRenderpass(surface, clear); 370 return true; 371 } 372 373 void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, 374 const VideoCore::TextureClear& clear) { 375 const bool is_color = surface.type != VideoCore::SurfaceType::Depth && 376 surface.type != VideoCore::SurfaceType::DepthStencil; 377 378 const auto color_format = is_color ? surface.pixel_format : PixelFormat::Invalid; 379 const auto depth_format = is_color ? PixelFormat::Invalid : surface.pixel_format; 380 const auto render_pass = renderpass_cache.GetRenderpass(color_format, depth_format, true); 381 382 const RecordParams params = { 383 .aspect = surface.Aspect(), 384 .pipeline_flags = surface.PipelineStageFlags(), 385 .src_access = surface.AccessFlags(), 386 .src_image = surface.Image(), 387 }; 388 389 scheduler.Record([params, is_color, clear, render_pass, 390 framebuffer = surface.Framebuffer()](vk::CommandBuffer cmdbuf) { 391 const vk::AccessFlags access_flag = 392 is_color ? vk::AccessFlagBits::eColorAttachmentRead | 393 vk::AccessFlagBits::eColorAttachmentWrite 394 : vk::AccessFlagBits::eDepthStencilAttachmentRead | 395 vk::AccessFlagBits::eDepthStencilAttachmentWrite; 396 397 const vk::PipelineStageFlags pipeline_flags = 398 is_color ? vk::PipelineStageFlagBits::eColorAttachmentOutput 399 : vk::PipelineStageFlagBits::eEarlyFragmentTests; 400 401 const vk::ImageMemoryBarrier pre_barrier = { 402 .srcAccessMask = params.src_access, 403 .dstAccessMask = access_flag, 404 .oldLayout = vk::ImageLayout::eGeneral, 405 .newLayout = vk::ImageLayout::eGeneral, 406 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 407 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 408 .image = params.src_image, 409 .subresourceRange = MakeSubresourceRange(params.aspect, clear.texture_level), 410 }; 411 412 const vk::ImageMemoryBarrier post_barrier = { 413 .srcAccessMask = access_flag, 414 .dstAccessMask = params.src_access, 415 .oldLayout = vk::ImageLayout::eGeneral, 416 .newLayout = vk::ImageLayout::eGeneral, 417 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 418 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 419 .image = params.src_image, 420 .subresourceRange = MakeSubresourceRange(params.aspect, clear.texture_level), 421 }; 422 423 const vk::Rect2D render_area = { 424 .offset{ 425 .x = static_cast<s32>(clear.texture_rect.left), 426 .y = static_cast<s32>(clear.texture_rect.bottom), 427 }, 428 .extent{ 429 .width = clear.texture_rect.GetWidth(), 430 .height = clear.texture_rect.GetHeight(), 431 }, 432 }; 433 434 const auto clear_value = MakeClearValue(clear.value); 435 436 const vk::RenderPassBeginInfo renderpass_begin_info = { 437 .renderPass = render_pass, 438 .framebuffer = framebuffer, 439 .renderArea = render_area, 440 .clearValueCount = 1, 441 .pClearValues = &clear_value, 442 }; 443 444 cmdbuf.pipelineBarrier(params.pipeline_flags, pipeline_flags, 445 vk::DependencyFlagBits::eByRegion, {}, {}, pre_barrier); 446 447 cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); 448 cmdbuf.endRenderPass(); 449 450 cmdbuf.pipelineBarrier(pipeline_flags, params.pipeline_flags, 451 vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier); 452 }); 453 } 454 455 bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, 456 const VideoCore::TextureCopy& copy) { 457 renderpass_cache.EndRendering(); 458 459 const RecordParams params = { 460 .aspect = source.Aspect(), 461 .filter = MakeFilter(source.pixel_format), 462 .pipeline_flags = source.PipelineStageFlags() | dest.PipelineStageFlags(), 463 .src_access = source.AccessFlags(), 464 .dst_access = dest.AccessFlags(), 465 .src_image = source.Image(), 466 .dst_image = dest.Image(), 467 }; 468 469 scheduler.Record([params, copy](vk::CommandBuffer cmdbuf) { 470 const vk::ImageCopy image_copy = { 471 .srcSubresource{ 472 .aspectMask = params.aspect, 473 .mipLevel = copy.src_level, 474 .baseArrayLayer = copy.src_layer, 475 .layerCount = 1, 476 }, 477 .srcOffset = {static_cast<s32>(copy.src_offset.x), static_cast<s32>(copy.src_offset.y), 478 0}, 479 .dstSubresource{ 480 .aspectMask = params.aspect, 481 .mipLevel = copy.dst_level, 482 .baseArrayLayer = copy.dst_layer, 483 .layerCount = 1, 484 }, 485 .dstOffset = {static_cast<s32>(copy.dst_offset.x), static_cast<s32>(copy.dst_offset.y), 486 0}, 487 .extent = {copy.extent.width, copy.extent.height, 1}, 488 }; 489 490 const bool self_copy = params.src_image == params.dst_image; 491 const vk::ImageLayout new_src_layout = 492 self_copy ? vk::ImageLayout::eGeneral : vk::ImageLayout::eTransferSrcOptimal; 493 const vk::ImageLayout new_dst_layout = 494 self_copy ? vk::ImageLayout::eGeneral : vk::ImageLayout::eTransferDstOptimal; 495 496 const std::array pre_barriers = { 497 vk::ImageMemoryBarrier{ 498 .srcAccessMask = params.src_access, 499 .dstAccessMask = vk::AccessFlagBits::eTransferRead, 500 .oldLayout = vk::ImageLayout::eGeneral, 501 .newLayout = new_src_layout, 502 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 503 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 504 .image = params.src_image, 505 .subresourceRange = MakeSubresourceRange(params.aspect, copy.src_level), 506 }, 507 vk::ImageMemoryBarrier{ 508 .srcAccessMask = params.dst_access, 509 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 510 .oldLayout = vk::ImageLayout::eGeneral, 511 .newLayout = new_dst_layout, 512 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 513 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 514 .image = params.dst_image, 515 .subresourceRange = MakeSubresourceRange(params.aspect, copy.dst_level), 516 }, 517 }; 518 const std::array post_barriers = { 519 vk::ImageMemoryBarrier{ 520 .srcAccessMask = vk::AccessFlagBits::eNone, 521 .dstAccessMask = vk::AccessFlagBits::eNone, 522 .oldLayout = new_src_layout, 523 .newLayout = vk::ImageLayout::eGeneral, 524 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 525 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 526 .image = params.src_image, 527 .subresourceRange = MakeSubresourceRange(params.aspect, copy.src_level), 528 }, 529 vk::ImageMemoryBarrier{ 530 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 531 .dstAccessMask = params.dst_access, 532 .oldLayout = new_dst_layout, 533 .newLayout = vk::ImageLayout::eGeneral, 534 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 535 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 536 .image = params.dst_image, 537 .subresourceRange = MakeSubresourceRange(params.aspect, copy.dst_level), 538 }, 539 }; 540 541 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 542 vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers); 543 544 cmdbuf.copyImage(params.src_image, new_src_layout, params.dst_image, new_dst_layout, 545 image_copy); 546 547 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 548 vk::DependencyFlagBits::eByRegion, {}, {}, post_barriers); 549 }); 550 551 return true; 552 } 553 554 bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, 555 const VideoCore::TextureBlit& blit) { 556 const bool is_depth_stencil = source.type == VideoCore::SurfaceType::DepthStencil; 557 const auto& depth_traits = instance.GetTraits(source.pixel_format); 558 if (is_depth_stencil && !depth_traits.blit_support) { 559 return blit_helper.BlitDepthStencil(source, dest, blit); 560 } 561 562 renderpass_cache.EndRendering(); 563 564 const RecordParams params = { 565 .aspect = source.Aspect(), 566 .filter = MakeFilter(source.pixel_format), 567 .pipeline_flags = source.PipelineStageFlags() | dest.PipelineStageFlags(), 568 .src_access = source.AccessFlags(), 569 .dst_access = dest.AccessFlags(), 570 .src_image = source.Image(), 571 .dst_image = dest.Image(), 572 }; 573 574 scheduler.Record([params, blit](vk::CommandBuffer cmdbuf) { 575 const std::array source_offsets = { 576 vk::Offset3D{static_cast<s32>(blit.src_rect.left), 577 static_cast<s32>(blit.src_rect.bottom), 0}, 578 vk::Offset3D{static_cast<s32>(blit.src_rect.right), static_cast<s32>(blit.src_rect.top), 579 1}, 580 }; 581 582 const std::array dest_offsets = { 583 vk::Offset3D{static_cast<s32>(blit.dst_rect.left), 584 static_cast<s32>(blit.dst_rect.bottom), 0}, 585 vk::Offset3D{static_cast<s32>(blit.dst_rect.right), static_cast<s32>(blit.dst_rect.top), 586 1}, 587 }; 588 589 const vk::ImageBlit blit_area = { 590 .srcSubresource{ 591 .aspectMask = params.aspect, 592 .mipLevel = blit.src_level, 593 .baseArrayLayer = blit.src_layer, 594 .layerCount = 1, 595 }, 596 .srcOffsets = source_offsets, 597 .dstSubresource{ 598 .aspectMask = params.aspect, 599 .mipLevel = blit.dst_level, 600 .baseArrayLayer = blit.dst_layer, 601 .layerCount = 1, 602 }, 603 .dstOffsets = dest_offsets, 604 }; 605 606 const std::array read_barriers = { 607 vk::ImageMemoryBarrier{ 608 .srcAccessMask = params.src_access, 609 .dstAccessMask = vk::AccessFlagBits::eTransferRead, 610 .oldLayout = vk::ImageLayout::eGeneral, 611 .newLayout = vk::ImageLayout::eTransferSrcOptimal, 612 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 613 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 614 .image = params.src_image, 615 .subresourceRange = MakeSubresourceRange(params.aspect, blit.src_level), 616 }, 617 vk::ImageMemoryBarrier{ 618 .srcAccessMask = params.dst_access, 619 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 620 .oldLayout = vk::ImageLayout::eGeneral, 621 .newLayout = vk::ImageLayout::eTransferDstOptimal, 622 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 623 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 624 .image = params.dst_image, 625 .subresourceRange = MakeSubresourceRange(params.aspect, blit.dst_level), 626 }, 627 }; 628 const std::array write_barriers = { 629 vk::ImageMemoryBarrier{ 630 .srcAccessMask = vk::AccessFlagBits::eTransferRead, 631 .dstAccessMask = params.src_access, 632 .oldLayout = vk::ImageLayout::eTransferSrcOptimal, 633 .newLayout = vk::ImageLayout::eGeneral, 634 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 635 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 636 .image = params.src_image, 637 .subresourceRange = MakeSubresourceRange(params.aspect, blit.src_level), 638 }, 639 vk::ImageMemoryBarrier{ 640 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 641 .dstAccessMask = params.dst_access, 642 .oldLayout = vk::ImageLayout::eTransferDstOptimal, 643 .newLayout = vk::ImageLayout::eGeneral, 644 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 645 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 646 .image = params.dst_image, 647 .subresourceRange = MakeSubresourceRange(params.aspect, blit.dst_level), 648 }, 649 }; 650 651 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 652 vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers); 653 654 cmdbuf.blitImage(params.src_image, vk::ImageLayout::eTransferSrcOptimal, params.dst_image, 655 vk::ImageLayout::eTransferDstOptimal, blit_area, params.filter); 656 657 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 658 vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers); 659 }); 660 661 return true; 662 } 663 664 void TextureRuntime::GenerateMipmaps(Surface& surface) { 665 if (VideoCore::IsCustomFormatCompressed(surface.custom_format)) { 666 LOG_ERROR(Render_Vulkan, "Generating mipmaps for compressed formats unsupported!"); 667 return; 668 } 669 670 renderpass_cache.EndRendering(); 671 672 auto [width, height] = surface.RealExtent(); 673 const u32 levels = surface.levels; 674 for (u32 i = 1; i < levels; i++) { 675 const Common::Rectangle<u32> src_rect{0, height, width, 0}; 676 width = width > 1 ? width >> 1 : 1; 677 height = height > 1 ? height >> 1 : 1; 678 const Common::Rectangle<u32> dst_rect{0, height, width, 0}; 679 680 const VideoCore::TextureBlit blit = { 681 .src_level = i - 1, 682 .dst_level = i, 683 .src_rect = src_rect, 684 .dst_rect = dst_rect, 685 }; 686 BlitTextures(surface, surface, blit); 687 } 688 } 689 690 bool TextureRuntime::NeedsConversion(VideoCore::PixelFormat format) const { 691 const FormatTraits traits = instance.GetTraits(format); 692 return traits.needs_conversion && 693 // DepthStencil formats are handled elsewhere due to de-interleaving. 694 traits.aspect != (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil); 695 } 696 697 void TextureRuntime::FreeDescriptorSetsWithImage(vk::ImageView image_view) { 698 texture_provider.FreeWithImage(image_view); 699 blit_helper.compute_provider.FreeWithImage(image_view); 700 blit_helper.compute_buffer_provider.FreeWithImage(image_view); 701 blit_helper.two_textures_provider.FreeWithImage(image_view); 702 } 703 704 Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params) 705 : SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()}, 706 scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(pixel_format)} { 707 708 if (pixel_format == VideoCore::PixelFormat::Invalid) { 709 return; 710 } 711 712 const bool is_mutable = pixel_format == VideoCore::PixelFormat::RGBA8; 713 const vk::Format format = traits.native; 714 715 ASSERT_MSG(format != vk::Format::eUndefined && levels >= 1, 716 "Image allocation parameters are invalid"); 717 718 boost::container::static_vector<vk::Image, 3> raw_images; 719 720 vk::ImageCreateFlags flags{}; 721 if (texture_type == VideoCore::TextureType::CubeMap) { 722 flags |= vk::ImageCreateFlagBits::eCubeCompatible; 723 } 724 if (is_mutable) { 725 flags |= vk::ImageCreateFlagBits::eMutableFormat; 726 } 727 728 const bool need_format_list = is_mutable && instance->IsImageFormatListSupported(); 729 handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage, 730 flags, traits.aspect, need_format_list, DebugName(false)); 731 raw_images.emplace_back(handles[0].image); 732 733 if (res_scale != 1) { 734 handles[1] = 735 MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format, 736 traits.usage, flags, traits.aspect, need_format_list, DebugName(true)); 737 raw_images.emplace_back(handles[1].image); 738 } 739 740 runtime->renderpass_cache.EndRendering(); 741 scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { 742 const auto barriers = MakeInitBarriers(aspect, raw_images); 743 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, 744 vk::PipelineStageFlagBits::eTopOfPipe, 745 vk::DependencyFlagBits::eByRegion, {}, {}, barriers); 746 }); 747 } 748 749 Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface, 750 const VideoCore::Material* mat) 751 : SurfaceBase{surface}, runtime{&runtime_}, instance{&runtime_.GetInstance()}, 752 scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(mat->format)} { 753 if (!traits.transfer_support) { 754 return; 755 } 756 757 const bool has_normal = mat && mat->Map(MapType::Normal); 758 const vk::Format format = traits.native; 759 760 boost::container::static_vector<vk::Image, 2> raw_images; 761 762 vk::ImageCreateFlags flags{}; 763 if (texture_type == VideoCore::TextureType::CubeMap) { 764 flags |= vk::ImageCreateFlagBits::eCubeCompatible; 765 } 766 767 const std::string debug_name = DebugName(false, true); 768 handles[0] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format, 769 traits.usage, flags, traits.aspect, false, debug_name); 770 raw_images.emplace_back(handles[0].image); 771 772 if (res_scale != 1) { 773 handles[1] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, 774 vk::Format::eR8G8B8A8Unorm, traits.usage, flags, traits.aspect, 775 false, debug_name); 776 raw_images.emplace_back(handles[1].image); 777 } 778 if (has_normal) { 779 handles[2] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format, 780 traits.usage, flags, traits.aspect, false, debug_name); 781 raw_images.emplace_back(handles[2].image); 782 } 783 784 runtime->renderpass_cache.EndRendering(); 785 scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { 786 const auto barriers = MakeInitBarriers(aspect, raw_images); 787 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, 788 vk::PipelineStageFlagBits::eTopOfPipe, 789 vk::DependencyFlagBits::eByRegion, {}, {}, barriers); 790 }); 791 792 custom_format = mat->format; 793 material = mat; 794 } 795 796 Surface::~Surface() { 797 if (!handles[0].image_view) { 798 return; 799 } 800 for (const auto& [alloc, image, image_view] : handles) { 801 if (image_view) { 802 runtime->FreeDescriptorSetsWithImage(*image_view); 803 } 804 if (image) { 805 vmaDestroyImage(instance->GetAllocator(), image, alloc); 806 } 807 } 808 if (copy_handle.image_view) { 809 vmaDestroyImage(instance->GetAllocator(), copy_handle.image, copy_handle.alloc); 810 } 811 } 812 813 void Surface::Upload(const VideoCore::BufferTextureCopy& upload, 814 const VideoCore::StagingData& staging) { 815 runtime->renderpass_cache.EndRendering(); 816 817 const RecordParams params = { 818 .aspect = Aspect(), 819 .pipeline_flags = PipelineStageFlags(), 820 .src_access = AccessFlags(), 821 .src_image = Image(0), 822 }; 823 824 scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = traits.native, params, 825 staging, upload](vk::CommandBuffer cmdbuf) { 826 boost::container::static_vector<vk::BufferImageCopy, 2> buffer_image_copies; 827 828 const auto rect = upload.texture_rect; 829 buffer_image_copies.emplace_back(vk::BufferImageCopy{ 830 .bufferOffset = upload.buffer_offset, 831 .bufferRowLength = rect.GetWidth(), 832 .bufferImageHeight = rect.GetHeight(), 833 .imageSubresource{ 834 .aspectMask = params.aspect, 835 .mipLevel = upload.texture_level, 836 .baseArrayLayer = 0, 837 .layerCount = 1, 838 }, 839 .imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0}, 840 .imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}, 841 }); 842 843 if (params.aspect & vk::ImageAspectFlagBits::eStencil) { 844 buffer_image_copies[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth; 845 846 vk::BufferImageCopy& stencil_copy = 847 buffer_image_copies.emplace_back(buffer_image_copies[0]); 848 stencil_copy = buffer_image_copies[0]; 849 stencil_copy.bufferOffset += UnpackDepthStencil(staging, format); 850 stencil_copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil; 851 } 852 853 const vk::ImageMemoryBarrier read_barrier = { 854 .srcAccessMask = params.src_access, 855 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 856 .oldLayout = vk::ImageLayout::eGeneral, 857 .newLayout = vk::ImageLayout::eTransferDstOptimal, 858 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 859 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 860 .image = params.src_image, 861 .subresourceRange = MakeSubresourceRange(params.aspect, upload.texture_level), 862 }; 863 const vk::ImageMemoryBarrier write_barrier = { 864 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 865 .dstAccessMask = params.src_access, 866 .oldLayout = vk::ImageLayout::eTransferDstOptimal, 867 .newLayout = vk::ImageLayout::eGeneral, 868 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 869 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 870 .image = params.src_image, 871 .subresourceRange = MakeSubresourceRange(params.aspect, upload.texture_level), 872 }; 873 874 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 875 vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier); 876 877 cmdbuf.copyBufferToImage(buffer, params.src_image, vk::ImageLayout::eTransferDstOptimal, 878 buffer_image_copies); 879 880 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 881 vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier); 882 }); 883 884 runtime->upload_buffer.Commit(staging.size); 885 886 if (res_scale != 1) { 887 const VideoCore::TextureBlit blit = { 888 .src_level = upload.texture_level, 889 .dst_level = upload.texture_level, 890 .src_rect = upload.texture_rect, 891 .dst_rect = upload.texture_rect * res_scale, 892 }; 893 894 BlitScale(blit, true); 895 } 896 } 897 898 void Surface::UploadCustom(const VideoCore::Material* material, u32 level) { 899 const u32 width = material->width; 900 const u32 height = material->height; 901 const auto color = material->textures[0]; 902 const Common::Rectangle rect{0U, height, width, 0U}; 903 904 const auto upload = [&](u32 index, VideoCore::CustomTexture* texture) { 905 const u64 custom_size = texture->data.size(); 906 const RecordParams params = { 907 .aspect = vk::ImageAspectFlagBits::eColor, 908 .pipeline_flags = PipelineStageFlags(), 909 .src_access = AccessFlags(), 910 .src_image = Image(index), 911 }; 912 913 const auto [data, offset, invalidate] = runtime->upload_buffer.Map(custom_size, 0); 914 std::memcpy(data, texture->data.data(), custom_size); 915 runtime->upload_buffer.Commit(custom_size); 916 917 scheduler->Record([buffer = runtime->upload_buffer.Handle(), level, params, rect, 918 offset = offset](vk::CommandBuffer cmdbuf) { 919 const vk::BufferImageCopy buffer_image_copy = { 920 .bufferOffset = offset, 921 .bufferRowLength = 0, 922 .bufferImageHeight = rect.GetHeight(), 923 .imageSubresource{ 924 .aspectMask = params.aspect, 925 .mipLevel = level, 926 .baseArrayLayer = 0, 927 .layerCount = 1, 928 }, 929 .imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0}, 930 .imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}, 931 }; 932 933 const vk::ImageMemoryBarrier read_barrier = { 934 .srcAccessMask = params.src_access, 935 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 936 .oldLayout = vk::ImageLayout::eGeneral, 937 .newLayout = vk::ImageLayout::eTransferDstOptimal, 938 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 939 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 940 .image = params.src_image, 941 .subresourceRange = MakeSubresourceRange(params.aspect, level), 942 }; 943 const vk::ImageMemoryBarrier write_barrier = { 944 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 945 .dstAccessMask = params.src_access, 946 .oldLayout = vk::ImageLayout::eTransferDstOptimal, 947 .newLayout = vk::ImageLayout::eGeneral, 948 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 949 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 950 .image = params.src_image, 951 .subresourceRange = MakeSubresourceRange(params.aspect, level), 952 }; 953 954 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 955 vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier); 956 957 cmdbuf.copyBufferToImage(buffer, params.src_image, vk::ImageLayout::eTransferDstOptimal, 958 buffer_image_copy); 959 960 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 961 vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier); 962 }); 963 }; 964 965 upload(0, color); 966 967 for (u32 i = 1; i < VideoCore::MAX_MAPS; i++) { 968 const auto texture = material->textures[i]; 969 if (!texture) { 970 continue; 971 } 972 upload(i + 1, texture); 973 } 974 } 975 976 void Surface::Download(const VideoCore::BufferTextureCopy& download, 977 const VideoCore::StagingData& staging) { 978 SCOPE_EXIT({ 979 scheduler->Finish(); 980 runtime->download_buffer.Commit(staging.size); 981 }); 982 983 runtime->renderpass_cache.EndRendering(); 984 985 if (pixel_format == PixelFormat::D24S8) { 986 runtime->blit_helper.DepthToBuffer(*this, runtime->download_buffer.Handle(), download); 987 return; 988 } 989 990 if (res_scale != 1) { 991 const VideoCore::TextureBlit blit = { 992 .src_level = download.texture_level, 993 .dst_level = download.texture_level, 994 .src_rect = download.texture_rect * res_scale, 995 .dst_rect = download.texture_rect, 996 }; 997 998 BlitScale(blit, false); 999 } 1000 1001 const RecordParams params = { 1002 .aspect = Aspect(), 1003 .pipeline_flags = PipelineStageFlags(), 1004 .src_access = AccessFlags(), 1005 .src_image = Image(0), 1006 }; 1007 1008 scheduler->Record( 1009 [buffer = runtime->download_buffer.Handle(), params, download](vk::CommandBuffer cmdbuf) { 1010 const auto rect = download.texture_rect; 1011 const vk::BufferImageCopy buffer_image_copy = { 1012 .bufferOffset = download.buffer_offset, 1013 .bufferRowLength = rect.GetWidth(), 1014 .bufferImageHeight = rect.GetHeight(), 1015 .imageSubresource{ 1016 .aspectMask = params.aspect, 1017 .mipLevel = download.texture_level, 1018 .baseArrayLayer = 0, 1019 .layerCount = 1, 1020 }, 1021 .imageOffset = {static_cast<s32>(rect.left), static_cast<s32>(rect.bottom), 0}, 1022 .imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}, 1023 }; 1024 1025 const vk::ImageMemoryBarrier read_barrier = { 1026 .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, 1027 .dstAccessMask = vk::AccessFlagBits::eTransferRead, 1028 .oldLayout = vk::ImageLayout::eGeneral, 1029 .newLayout = vk::ImageLayout::eTransferSrcOptimal, 1030 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1031 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1032 .image = params.src_image, 1033 .subresourceRange = MakeSubresourceRange(params.aspect, download.texture_level), 1034 }; 1035 const vk::ImageMemoryBarrier image_write_barrier = { 1036 .srcAccessMask = vk::AccessFlagBits::eNone, 1037 .dstAccessMask = vk::AccessFlagBits::eMemoryWrite, 1038 .oldLayout = vk::ImageLayout::eTransferSrcOptimal, 1039 .newLayout = vk::ImageLayout::eGeneral, 1040 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1041 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1042 .image = params.src_image, 1043 .subresourceRange = MakeSubresourceRange(params.aspect, download.texture_level), 1044 }; 1045 const vk::MemoryBarrier memory_write_barrier = { 1046 .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, 1047 .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, 1048 }; 1049 1050 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 1051 vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier); 1052 1053 cmdbuf.copyImageToBuffer(params.src_image, vk::ImageLayout::eTransferSrcOptimal, buffer, 1054 buffer_image_copy); 1055 1056 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 1057 vk::DependencyFlagBits::eByRegion, memory_write_barrier, {}, 1058 image_write_barrier); 1059 }); 1060 } 1061 1062 void Surface::ScaleUp(u32 new_scale) { 1063 if (res_scale == new_scale || new_scale == 1) { 1064 return; 1065 } 1066 1067 res_scale = new_scale; 1068 1069 const bool is_mutable = pixel_format == VideoCore::PixelFormat::RGBA8; 1070 1071 vk::ImageCreateFlags flags{}; 1072 if (texture_type == VideoCore::TextureType::CubeMap) { 1073 flags |= vk::ImageCreateFlagBits::eCubeCompatible; 1074 } 1075 if (is_mutable) { 1076 flags |= vk::ImageCreateFlagBits::eMutableFormat; 1077 } 1078 1079 handles[1] = 1080 MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, 1081 traits.native, traits.usage, flags, traits.aspect, false, DebugName(true)); 1082 1083 runtime->renderpass_cache.EndRendering(); 1084 scheduler->Record( 1085 [raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { 1086 const auto barriers = MakeInitBarriers(aspect, raw_images); 1087 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, 1088 vk::PipelineStageFlagBits::eTopOfPipe, 1089 vk::DependencyFlagBits::eByRegion, {}, {}, barriers); 1090 }); 1091 LOG_INFO(HW_GPU, "Surface scale up!"); 1092 for (u32 level = 0; level < levels; level++) { 1093 const VideoCore::TextureBlit blit = { 1094 .src_level = level, 1095 .dst_level = level, 1096 .src_rect = GetRect(level), 1097 .dst_rect = GetScaledRect(level), 1098 }; 1099 BlitScale(blit, true); 1100 } 1101 } 1102 1103 u32 Surface::GetInternalBytesPerPixel() const { 1104 // Request 5 bytes for D24S8 as well because we can use the 1105 // extra space when deinterleaving the data during upload 1106 if (traits.native == vk::Format::eD24UnormS8Uint) { 1107 return 5; 1108 } 1109 1110 return vk::blockSize(traits.native); 1111 } 1112 1113 vk::AccessFlags Surface::AccessFlags() const noexcept { 1114 const bool is_color = static_cast<bool>(Aspect() & vk::ImageAspectFlagBits::eColor); 1115 const vk::AccessFlags attachment_flags = 1116 is_color 1117 ? vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite 1118 : vk::AccessFlagBits::eDepthStencilAttachmentRead | 1119 vk::AccessFlagBits::eDepthStencilAttachmentWrite; 1120 1121 return vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead | 1122 vk::AccessFlagBits::eTransferWrite | 1123 (is_framebuffer ? attachment_flags : vk::AccessFlagBits::eNone) | 1124 (is_storage ? vk::AccessFlagBits::eShaderWrite : vk::AccessFlagBits::eNone); 1125 } 1126 1127 vk::PipelineStageFlags Surface::PipelineStageFlags() const noexcept { 1128 const bool is_color = static_cast<bool>(Aspect() & vk::ImageAspectFlagBits::eColor); 1129 const vk::PipelineStageFlags attachment_flags = 1130 is_color ? vk::PipelineStageFlagBits::eColorAttachmentOutput 1131 : vk::PipelineStageFlagBits::eEarlyFragmentTests | 1132 vk::PipelineStageFlagBits::eLateFragmentTests; 1133 1134 return vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eFragmentShader | 1135 (is_framebuffer ? attachment_flags : vk::PipelineStageFlagBits::eNone) | 1136 (is_storage ? vk::PipelineStageFlagBits::eComputeShader 1137 : vk::PipelineStageFlagBits::eNone); 1138 } 1139 1140 vk::Image Surface::Image(u32 index) const noexcept { 1141 const vk::Image image = handles[index].image; 1142 if (!image) { 1143 return handles[0].image; 1144 } 1145 return image; 1146 } 1147 1148 vk::ImageView Surface::CopyImageView() noexcept { 1149 vk::ImageLayout copy_layout = vk::ImageLayout::eGeneral; 1150 if (!copy_handle.image) { 1151 vk::ImageCreateFlags flags{}; 1152 if (texture_type == VideoCore::TextureType::CubeMap) { 1153 flags |= vk::ImageCreateFlagBits::eCubeCompatible; 1154 } 1155 copy_handle = 1156 MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, 1157 traits.native, traits.usage, flags, traits.aspect, false); 1158 copy_layout = vk::ImageLayout::eUndefined; 1159 } 1160 1161 runtime->renderpass_cache.EndRendering(); 1162 1163 const RecordParams params = { 1164 .aspect = Aspect(), 1165 .pipeline_flags = PipelineStageFlags(), 1166 .src_access = AccessFlags(), 1167 .src_image = Image(), 1168 .dst_image = copy_handle.image, 1169 }; 1170 1171 scheduler->Record([params, copy_layout, levels = this->levels, width = GetScaledWidth(), 1172 height = GetScaledHeight()](vk::CommandBuffer cmdbuf) { 1173 std::array pre_barriers = { 1174 vk::ImageMemoryBarrier{ 1175 .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, 1176 .dstAccessMask = vk::AccessFlagBits::eTransferRead, 1177 .oldLayout = vk::ImageLayout::eGeneral, 1178 .newLayout = vk::ImageLayout::eTransferSrcOptimal, 1179 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1180 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1181 .image = params.src_image, 1182 .subresourceRange = MakeSubresourceRange(params.aspect, 0, levels), 1183 }, 1184 vk::ImageMemoryBarrier{ 1185 .srcAccessMask = vk::AccessFlagBits::eShaderRead, 1186 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 1187 .oldLayout = copy_layout, 1188 .newLayout = vk::ImageLayout::eTransferDstOptimal, 1189 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1190 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1191 .image = params.dst_image, 1192 .subresourceRange = MakeSubresourceRange(params.aspect, 0, levels), 1193 }, 1194 }; 1195 std::array post_barriers = { 1196 vk::ImageMemoryBarrier{ 1197 .srcAccessMask = vk::AccessFlagBits::eTransferRead, 1198 .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, 1199 .oldLayout = vk::ImageLayout::eTransferSrcOptimal, 1200 .newLayout = vk::ImageLayout::eGeneral, 1201 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1202 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1203 .image = params.src_image, 1204 .subresourceRange = MakeSubresourceRange(params.aspect, 0, levels), 1205 }, 1206 vk::ImageMemoryBarrier{ 1207 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 1208 .dstAccessMask = vk::AccessFlagBits::eShaderRead, 1209 .oldLayout = vk::ImageLayout::eTransferDstOptimal, 1210 .newLayout = vk::ImageLayout::eGeneral, 1211 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1212 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1213 .image = params.dst_image, 1214 .subresourceRange = MakeSubresourceRange(params.aspect, 0, levels), 1215 }, 1216 }; 1217 1218 boost::container::small_vector<vk::ImageCopy, 3> image_copies; 1219 for (u32 level = 0; level < levels; level++) { 1220 image_copies.push_back(vk::ImageCopy{ 1221 .srcSubresource{ 1222 .aspectMask = vk::ImageAspectFlagBits::eColor, 1223 .mipLevel = level, 1224 .baseArrayLayer = 0, 1225 .layerCount = 1, 1226 }, 1227 .srcOffset = {0, 0, 0}, 1228 .dstSubresource{ 1229 .aspectMask = vk::ImageAspectFlagBits::eColor, 1230 .mipLevel = level, 1231 .baseArrayLayer = 0, 1232 .layerCount = 1, 1233 }, 1234 .dstOffset = {0, 0, 0}, 1235 .extent = {width >> level, height >> level, 1}, 1236 }); 1237 } 1238 1239 cmdbuf.pipelineBarrier(params.pipeline_flags, vk::PipelineStageFlagBits::eTransfer, 1240 vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers); 1241 1242 cmdbuf.copyImage(params.src_image, vk::ImageLayout::eTransferSrcOptimal, params.dst_image, 1243 vk::ImageLayout::eTransferDstOptimal, image_copies); 1244 1245 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, 1246 vk::DependencyFlagBits::eByRegion, {}, {}, post_barriers); 1247 }); 1248 1249 return copy_handle.image_view.get(); 1250 } 1251 1252 vk::ImageView Surface::ImageView(u32 index) const noexcept { 1253 const auto& image_view = handles[index].image_view.get(); 1254 if (!image_view) { 1255 return handles[0].image_view.get(); 1256 } 1257 return image_view; 1258 } 1259 1260 vk::ImageView Surface::FramebufferView() noexcept { 1261 is_framebuffer = true; 1262 return ImageView(); 1263 } 1264 1265 vk::ImageView Surface::DepthView() noexcept { 1266 if (depth_view) { 1267 return depth_view.get(); 1268 } 1269 1270 const vk::ImageViewCreateInfo view_info = { 1271 .image = Image(), 1272 .viewType = vk::ImageViewType::e2D, 1273 .format = instance->GetTraits(pixel_format).native, 1274 .subresourceRange{ 1275 .aspectMask = vk::ImageAspectFlagBits::eDepth, 1276 .baseMipLevel = 0, 1277 .levelCount = VK_REMAINING_MIP_LEVELS, 1278 .baseArrayLayer = 0, 1279 .layerCount = VK_REMAINING_ARRAY_LAYERS, 1280 }, 1281 }; 1282 1283 depth_view = instance->GetDevice().createImageViewUnique(view_info); 1284 return depth_view.get(); 1285 } 1286 1287 vk::ImageView Surface::StencilView() noexcept { 1288 if (stencil_view) { 1289 return stencil_view.get(); 1290 } 1291 1292 const vk::ImageViewCreateInfo view_info = { 1293 .image = Image(), 1294 .viewType = vk::ImageViewType::e2D, 1295 .format = instance->GetTraits(pixel_format).native, 1296 .subresourceRange{ 1297 .aspectMask = vk::ImageAspectFlagBits::eStencil, 1298 .baseMipLevel = 0, 1299 .levelCount = VK_REMAINING_MIP_LEVELS, 1300 .baseArrayLayer = 0, 1301 .layerCount = VK_REMAINING_ARRAY_LAYERS, 1302 }, 1303 }; 1304 1305 stencil_view = instance->GetDevice().createImageViewUnique(view_info); 1306 return stencil_view.get(); 1307 } 1308 1309 vk::ImageView Surface::StorageView() noexcept { 1310 if (storage_view) { 1311 return storage_view.get(); 1312 } 1313 1314 if (pixel_format != VideoCore::PixelFormat::RGBA8) { 1315 LOG_WARNING(Render_Vulkan, 1316 "Attempted to retrieve storage view from unsupported surface with format {}", 1317 VideoCore::PixelFormatAsString(pixel_format)); 1318 return ImageView(); 1319 } 1320 1321 is_storage = true; 1322 1323 const vk::ImageViewCreateInfo storage_view_info = { 1324 .image = Image(), 1325 .viewType = vk::ImageViewType::e2D, 1326 .format = vk::Format::eR32Uint, 1327 .subresourceRange{ 1328 .aspectMask = vk::ImageAspectFlagBits::eColor, 1329 .baseMipLevel = 0, 1330 .levelCount = VK_REMAINING_MIP_LEVELS, 1331 .baseArrayLayer = 0, 1332 .layerCount = VK_REMAINING_ARRAY_LAYERS, 1333 }, 1334 }; 1335 storage_view = instance->GetDevice().createImageViewUnique(storage_view_info); 1336 return storage_view.get(); 1337 } 1338 1339 vk::Framebuffer Surface::Framebuffer() noexcept { 1340 const u32 index = res_scale == 1 ? 0u : 1u; 1341 if (framebuffers[index]) { 1342 return framebuffers[index].get(); 1343 } 1344 1345 const bool is_depth = type == SurfaceType::Depth || type == SurfaceType::DepthStencil; 1346 const auto color_format = is_depth ? PixelFormat::Invalid : pixel_format; 1347 const auto depth_format = is_depth ? pixel_format : PixelFormat::Invalid; 1348 const auto render_pass = 1349 runtime->renderpass_cache.GetRenderpass(color_format, depth_format, false); 1350 const auto attachments = std::array{ImageView()}; 1351 framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(), 1352 GetScaledHeight(), attachments); 1353 return framebuffers[index].get(); 1354 } 1355 1356 void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { 1357 const FormatTraits& depth_traits = instance->GetTraits(pixel_format); 1358 const bool is_depth_stencil = pixel_format == PixelFormat::D24S8; 1359 if (is_depth_stencil && !depth_traits.blit_support) { 1360 LOG_WARNING(Render_Vulkan, "Depth scale unsupported by hardware"); 1361 return; 1362 } 1363 1364 scheduler->Record([src_image = Image(!up_scale), aspect = Aspect(), 1365 filter = MakeFilter(pixel_format), dst_image = Image(up_scale), 1366 blit](vk::CommandBuffer render_cmdbuf) { 1367 const std::array source_offsets = { 1368 vk::Offset3D{static_cast<s32>(blit.src_rect.left), 1369 static_cast<s32>(blit.src_rect.bottom), 0}, 1370 vk::Offset3D{static_cast<s32>(blit.src_rect.right), static_cast<s32>(blit.src_rect.top), 1371 1}, 1372 }; 1373 1374 const std::array dest_offsets = { 1375 vk::Offset3D{static_cast<s32>(blit.dst_rect.left), 1376 static_cast<s32>(blit.dst_rect.bottom), 0}, 1377 vk::Offset3D{static_cast<s32>(blit.dst_rect.right), static_cast<s32>(blit.dst_rect.top), 1378 1}, 1379 }; 1380 1381 const vk::ImageBlit blit_area = { 1382 .srcSubresource{ 1383 .aspectMask = aspect, 1384 .mipLevel = blit.src_level, 1385 .baseArrayLayer = blit.src_layer, 1386 .layerCount = 1, 1387 }, 1388 .srcOffsets = source_offsets, 1389 .dstSubresource{ 1390 .aspectMask = aspect, 1391 .mipLevel = blit.dst_level, 1392 .baseArrayLayer = blit.dst_layer, 1393 .layerCount = 1, 1394 }, 1395 .dstOffsets = dest_offsets, 1396 }; 1397 1398 const std::array read_barriers = { 1399 vk::ImageMemoryBarrier{ 1400 .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, 1401 .dstAccessMask = vk::AccessFlagBits::eTransferRead, 1402 .oldLayout = vk::ImageLayout::eGeneral, 1403 .newLayout = vk::ImageLayout::eTransferSrcOptimal, 1404 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1405 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1406 .image = src_image, 1407 .subresourceRange = MakeSubresourceRange(aspect, blit.src_level), 1408 }, 1409 vk::ImageMemoryBarrier{ 1410 .srcAccessMask = vk::AccessFlagBits::eShaderRead | 1411 vk::AccessFlagBits::eDepthStencilAttachmentRead | 1412 vk::AccessFlagBits::eColorAttachmentRead | 1413 vk::AccessFlagBits::eTransferRead, 1414 .dstAccessMask = vk::AccessFlagBits::eTransferWrite, 1415 .oldLayout = vk::ImageLayout::eGeneral, 1416 .newLayout = vk::ImageLayout::eTransferDstOptimal, 1417 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1418 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1419 .image = dst_image, 1420 .subresourceRange = MakeSubresourceRange(aspect, blit.dst_level), 1421 }, 1422 }; 1423 const std::array write_barriers = { 1424 vk::ImageMemoryBarrier{ 1425 .srcAccessMask = vk::AccessFlagBits::eNone, 1426 .dstAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead, 1427 .oldLayout = vk::ImageLayout::eTransferSrcOptimal, 1428 .newLayout = vk::ImageLayout::eGeneral, 1429 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1430 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1431 .image = src_image, 1432 .subresourceRange = MakeSubresourceRange(aspect, blit.src_level), 1433 }, 1434 vk::ImageMemoryBarrier{ 1435 .srcAccessMask = vk::AccessFlagBits::eTransferWrite, 1436 .dstAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead, 1437 .oldLayout = vk::ImageLayout::eTransferDstOptimal, 1438 .newLayout = vk::ImageLayout::eGeneral, 1439 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1440 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1441 .image = dst_image, 1442 .subresourceRange = MakeSubresourceRange(aspect, blit.dst_level), 1443 }, 1444 }; 1445 1446 render_cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, 1447 vk::PipelineStageFlagBits::eTransfer, 1448 vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers); 1449 1450 render_cmdbuf.blitImage(src_image, vk::ImageLayout::eTransferSrcOptimal, dst_image, 1451 vk::ImageLayout::eTransferDstOptimal, blit_area, filter); 1452 1453 render_cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, 1454 vk::PipelineStageFlagBits::eAllCommands, 1455 vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers); 1456 }); 1457 } 1458 1459 Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params, 1460 Surface* color, Surface* depth) 1461 : VideoCore::FramebufferParams{params}, res_scale{color ? color->res_scale 1462 : (depth ? depth->res_scale : 1u)} { 1463 auto& renderpass_cache = runtime.GetRenderpassCache(); 1464 if (shadow_rendering && !color) { 1465 return; 1466 } 1467 1468 width = height = std::numeric_limits<u32>::max(); 1469 1470 const auto prepare = [&](u32 index, Surface* surface) { 1471 const VideoCore::Extent extent = surface->RealExtent(); 1472 width = std::min(width, extent.width); 1473 height = std::min(height, extent.height); 1474 if (!shadow_rendering) { 1475 formats[index] = surface->pixel_format; 1476 } 1477 images[index] = surface->Image(); 1478 aspects[index] = surface->Aspect(); 1479 image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView(); 1480 }; 1481 1482 boost::container::static_vector<vk::ImageView, 2> attachments; 1483 1484 if (color) { 1485 prepare(0, color); 1486 attachments.emplace_back(image_views[0]); 1487 } 1488 1489 if (depth) { 1490 prepare(1, depth); 1491 attachments.emplace_back(image_views[1]); 1492 } 1493 1494 const vk::Device device = runtime.GetInstance().GetDevice(); 1495 if (shadow_rendering) { 1496 render_pass = 1497 renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false); 1498 framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(), 1499 color->GetScaledHeight(), {}); 1500 } else { 1501 render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false); 1502 framebuffer = MakeFramebuffer(device, render_pass, width, height, attachments); 1503 } 1504 } 1505 1506 Framebuffer::~Framebuffer() = default; 1507 1508 Sampler::Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params) { 1509 using TextureConfig = VideoCore::SamplerParams::TextureConfig; 1510 1511 const Instance& instance = runtime.GetInstance(); 1512 const vk::PhysicalDeviceProperties properties = instance.GetPhysicalDevice().getProperties(); 1513 const bool use_border_color = 1514 instance.IsCustomBorderColorSupported() && (params.wrap_s == TextureConfig::ClampToBorder || 1515 params.wrap_t == TextureConfig::ClampToBorder); 1516 1517 const Common::Vec4f color = PicaToVK::ColorRGBA8(params.border_color); 1518 const vk::SamplerCustomBorderColorCreateInfoEXT border_color_info = { 1519 .customBorderColor = MakeClearColorValue(color), 1520 .format = vk::Format::eUndefined, 1521 }; 1522 1523 const vk::Filter mag_filter = PicaToVK::TextureFilterMode(params.mag_filter); 1524 const vk::Filter min_filter = PicaToVK::TextureFilterMode(params.min_filter); 1525 const vk::SamplerMipmapMode mipmap_mode = PicaToVK::TextureMipFilterMode(params.mip_filter); 1526 const vk::SamplerAddressMode wrap_u = PicaToVK::WrapMode(params.wrap_s); 1527 const vk::SamplerAddressMode wrap_v = PicaToVK::WrapMode(params.wrap_t); 1528 const float lod_min = static_cast<float>(params.lod_min); 1529 const float lod_max = static_cast<float>(params.lod_max); 1530 1531 const vk::SamplerCreateInfo sampler_info = { 1532 .pNext = use_border_color ? &border_color_info : nullptr, 1533 .magFilter = mag_filter, 1534 .minFilter = min_filter, 1535 .mipmapMode = mipmap_mode, 1536 .addressModeU = wrap_u, 1537 .addressModeV = wrap_v, 1538 .mipLodBias = 0, 1539 .anisotropyEnable = instance.IsAnisotropicFilteringSupported(), 1540 .maxAnisotropy = properties.limits.maxSamplerAnisotropy, 1541 .compareEnable = false, 1542 .compareOp = vk::CompareOp::eAlways, 1543 .minLod = lod_min, 1544 .maxLod = lod_max, 1545 .borderColor = 1546 use_border_color ? vk::BorderColor::eFloatCustomEXT : vk::BorderColor::eIntOpaqueBlack, 1547 .unnormalizedCoordinates = false, 1548 }; 1549 sampler = instance.GetDevice().createSamplerUnique(sampler_info); 1550 } 1551 1552 Sampler::~Sampler() = default; 1553 1554 DebugScope::DebugScope(TextureRuntime& runtime, Common::Vec4f color, std::string_view label) 1555 : scheduler{runtime.GetScheduler()}, has_debug_tool{ 1556 runtime.GetInstance().HasDebuggingToolAttached()} { 1557 if (!has_debug_tool) { 1558 return; 1559 } 1560 scheduler.Record([color, label = std::string(label)](vk::CommandBuffer cmdbuf) { 1561 const vk::DebugUtilsLabelEXT debug_label = { 1562 .pLabelName = label.data(), 1563 .color = std::array{color[0], color[1], color[2], color[3]}, 1564 }; 1565 cmdbuf.beginDebugUtilsLabelEXT(debug_label); 1566 }); 1567 } 1568 1569 DebugScope::~DebugScope() { 1570 if (!has_debug_tool) { 1571 return; 1572 } 1573 scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.endDebugUtilsLabelEXT(); }); 1574 } 1575 1576 } // namespace Vulkan