From a6e506c32869ee01bb348dc9bcae73dc08f035ab Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 10 Aug 2026 01:29:27 -0400 Subject: [PATCH] [TEST] Bring the unswizzle work from tiled-gpu-v2 --- src/video_core/host_shaders/CMakeLists.txt | 2 + .../block_linear_unswizzle_2d_buffer.comp | 104 ++++ .../block_linear_unswizzle_3d_buffer.comp | 105 ++++ .../renderer_vulkan/vk_compute_pass.cpp | 477 ++++++++++++++++++ .../renderer_vulkan/vk_compute_pass.h | 38 ++ .../renderer_vulkan/vk_texture_cache.cpp | 20 + .../renderer_vulkan/vk_texture_cache.h | 2 + 7 files changed, 748 insertions(+) create mode 100644 src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp create mode 100644 src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 5b8f47a7a6..6be3e3bd06 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -17,11 +17,13 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp + ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp ${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp + ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag diff --git a/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp new file mode 100644 index 0000000000..3fc71e05b6 --- /dev/null +++ b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 430 + +#extension GL_EXT_shader_16bit_storage : require +#extension GL_EXT_shader_8bit_storage : require + +#define BINDING_INPUT_BUFFER 0 +#define BINDING_OUTPUT_BUFFER 1 + +layout(push_constant) uniform PushConstants { + uvec3 dim; + uint bytes_per_block_log2; + + uvec3 origin; + uint layer_stride; + + uint block_size; + uint x_shift; + uint block_height; + uint block_height_mask; +} pc; + +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; + +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { + uint out_u32[]; +}; + +layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT; + +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); + +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + +uint SwizzleOffset(uvec2 pos) { + pos = pos & SWIZZLE_MASK; + return SwizzleTable(pos.y * 64u + pos.x); +} + +uvec4 ReadTexel(uint offset) { + switch (pc.bytes_per_block_log2) { + case 2u: + return uvec4(u32data[offset / 4u], 0u, 0u, 0u); + case 3u: + return uvec4(u64data[offset / 8u], 0u, 0u); + case 4u: + return u128data[offset / 16u]; + } + return uvec4(0u); +} + +void main() { + uvec3 coord = gl_GlobalInvocationID; + if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) { + return; + } + + uvec3 pos = coord + pc.origin; + pos.x <<= pc.bytes_per_block_log2; + + uint swizzle = SwizzleOffset(pos.xy); + uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + + uint offset = 0u; + offset += pos.z * pc.layer_stride; + offset += (block_y >> pc.block_height) * pc.block_size; + offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift; + offset += swizzle; + + uvec4 texel = ReadTexel(offset); + + uint words = 1u << (pc.bytes_per_block_log2 - 2u); + uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y; + uint out_idx = linear_index * words; + + out_u32[out_idx] = texel.x; + if (words > 1u) { + out_u32[out_idx + 1u] = texel.y; + } + if (words > 2u) { + out_u32[out_idx + 2u] = texel.z; + out_u32[out_idx + 3u] = texel.w; + } +} diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp new file mode 100644 index 0000000000..7333568eb7 --- /dev/null +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 430 + +#define BINDING_INPUT_BUFFER 0 +#define BINDING_OUTPUT_BUFFER 1 + +layout(push_constant) uniform PushConstants { + uvec3 dim; + uint bytes_per_block_log2; + + uvec3 origin; + uint slice_size; + + uint block_size; + uint x_shift; + uint block_height; + uint block_height_mask; + + uint block_depth; + uint block_depth_mask; +} pc; + +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; + +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { + uint out_u32[]; +}; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT; + +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); + +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + +uint SwizzleOffset(uvec2 pos) { + pos = pos & SWIZZLE_MASK; + return SwizzleTable(pos.y * 64u + pos.x); +} + +uvec4 ReadTexel(uint offset) { + switch (pc.bytes_per_block_log2) { + case 2u: + return uvec4(u32data[offset / 4u], 0u, 0u, 0u); + case 3u: + return uvec4(u64data[offset / 8u], 0u, 0u); + case 4u: + return u128data[offset / 16u]; + } + return uvec4(0u); +} + +void main() { + uvec3 coord = gl_GlobalInvocationID; + if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) { + return; + } + + uvec3 pos = coord + pc.origin; + pos.x <<= pc.bytes_per_block_log2; + + uint swizzle = SwizzleOffset(pos.xy); + uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + + uint offset = 0u; + offset += (pos.z >> pc.block_depth) * pc.slice_size; + offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height); + offset += (block_y >> pc.block_height) * pc.block_size; + offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift; + offset += swizzle; + + uvec4 texel = ReadTexel(offset); + + uint words = 1u << (pc.bytes_per_block_log2 - 2u); + uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y; + uint out_idx = linear_index * words; + + out_u32[out_idx] = texel.x; + if (words > 1u) { + out_u32[out_idx + 1u] = texel.y; + } + if (words > 2u) { + out_u32[out_idx + 2u] = texel.z; + out_u32[out_idx + 3u] = texel.w; + } +} diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 80c434dc8f..084e2b57ee 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -22,7 +22,9 @@ #include "video_core/host_shaders/resolve_conditional_render_comp_spv.h" #include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/surface.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -872,4 +874,479 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( }); } +namespace { +constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0; +constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1; + +struct alignas(16) BlockLinearUnswizzle2DPushConstants { + std::array dim; + u32 bytes_per_block_log2; + std::array origin; + u32 layer_stride; + u32 block_size; + u32 x_shift; + u32 block_height; + u32 block_height_mask; +}; +static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128); + +constexpr std::array BL2D_BINDINGS{{ + { + .binding = BL2D_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = BL2D_BINDING_OUTPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, +}}; + +constexpr std::array BL2D_TEMPLATE{{ + { + .dstBinding = BL2D_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = BL2D_BINDING_OUTPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, +}}; + +constexpr DescriptorBankInfo BL2D_BANK_INFO{ + .uniform_buffers = 0, + .storage_buffers = 2, + .texture_buffers = 0, + .image_buffers = 0, + .textures = 0, + .images = 0, + .score = 2, +}; +} // Anonymous namespace + +BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE, + BL2D_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV), + scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default; + +bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) { + if (info.type != VideoCommon::ImageType::e2D) { + return false; + } + if (info.resources.levels != 1 || info.resources.layers != 1) { + return false; + } + if (info.num_samples > 1) { + return false; + } + if (VideoCore::Surface::IsPixelFormatASTC(info.format) || + VideoCore::Surface::IsPixelFormatBCn(info.format)) { + return false; + } + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); + if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) { + return false; + } + return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 && + VideoCore::Surface::DefaultBlockHeight(info.format) == 1; +} + +void BlockLinearUnswizzle2DPass::Unswizzle( + Image& image, const StagingBufferRef& swizzled, + std::span swizzles) { + if (swizzles.empty()) { + return; + } + const VideoCommon::SwizzleParameters& sw = swizzles.front(); + const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info); + + const u32 width = sw.num_tiles.width; + const u32 height = sw.num_tiles.height; + const u32 depth = image.info.resources.layers; + const u32 bytes_per_block = 1u << params.bytes_per_block_log2; + const VkDeviceSize output_size = + static_cast(width) * height * depth * bytes_per_block; + + const StagingBufferRef output = + staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); + + BlockLinearUnswizzle2DPushConstants pc{}; + pc.dim = {width, height, depth}; + pc.bytes_per_block_log2 = params.bytes_per_block_log2; + pc.origin = params.origin; + pc.layer_stride = params.layer_stride; + pc.block_size = params.block_size; + pc.x_shift = params.x_shift; + pc.block_height = params.block_height; + pc.block_height_mask = params.block_height_mask; + + scheduler.RequestOutsideRenderPassOperationContext(); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset, + image.guest_size_bytes - sw.buffer_offset); + compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); + + const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); + const VkDescriptorSet set = descriptor_allocator.Commit(); + + const u32 gx = Common::DivCeil(width, 16u); + const u32 gy = Common::DivCeil(height, 8u); + const bool is_initialized = image.ExchangeInitialization(); + + const VkBuffer out_buffer = output.buffer; + const VkDeviceSize out_offset = output.offset; + const VkImage dst_image = image.Handle(); + const VkImageAspectFlags aspect = image.AspectMask(); + + scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer, + out_offset, dst_image, aspect, width, height, + is_initialized](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { + return; + } + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); + cmdbuf.Dispatch(gx, gy, depth); + + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = out_offset, + .size = output_size, + }; + const VkImageMemoryBarrier pre_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT + : VK_ACCESS_NONE), + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE + : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + + const VkBufferImageCopy copy{ + .bufferOffset = out_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = aspect, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = depth, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = {width, height, 1}, + }; + cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + + const VkImageMemoryBarrier post_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, {}, {}, post_copy); + }); +} + +namespace { +constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0; +constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1; + +struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants { + std::array dim; + u32 bytes_per_block_log2; + std::array origin; + u32 slice_size; + u32 block_size; + u32 x_shift; + u32 block_height; + u32 block_height_mask; + u32 block_depth; + u32 block_depth_mask; +}; +static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128); + +constexpr std::array BL3DB_BINDINGS{{ + { + .binding = BL3DB_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = BL3DB_BINDING_OUTPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, +}}; + +constexpr std::array BL3DB_TEMPLATE{{ + { + .dstBinding = BL3DB_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = BL3DB_BINDING_OUTPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, +}}; + +constexpr DescriptorBankInfo BL3DB_BANK_INFO{ + .uniform_buffers = 0, + .storage_buffers = 2, + .texture_buffers = 0, + .image_buffers = 0, + .textures = 0, + .images = 0, + .score = 2, +}; +} // Anonymous namespace + +BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE, + BL3DB_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV), + scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default; + +bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device, + const VideoCommon::ImageInfo& info) { + if (info.type != VideoCommon::ImageType::e3D) { + return false; + } + if (info.resources.levels != 1 || info.resources.layers != 1) { + return false; + } + if (info.num_samples > 1) { + return false; + } + if (info.size.depth <= 1) { + return false; + } + if (VideoCore::Surface::IsPixelFormatASTC(info.format)) { + return false; + } + if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) { + return false; + } + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format); + return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16; +} + +void BlockLinearUnswizzle3DBufferPass::Unswizzle( + Image& image, const StagingBufferRef& swizzled, + std::span swizzles) { + if (swizzles.empty()) { + return; + } + + const VideoCommon::SwizzleParameters& sw = swizzles.front(); + const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info); + + const u32 blocks_x = sw.num_tiles.width; + const u32 blocks_y = sw.num_tiles.height; + const u32 blocks_z = sw.num_tiles.depth; + const u32 bytes_per_block = 1u << params.bytes_per_block_log2; + const VkDeviceSize output_size = + static_cast(blocks_x) * blocks_y * blocks_z * bytes_per_block; + + const StagingBufferRef output = + staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); + + BlockLinearUnswizzle3DBufferPushConstants pc{}; + pc.dim = {blocks_x, blocks_y, blocks_z}; + pc.bytes_per_block_log2 = params.bytes_per_block_log2; + pc.origin = params.origin; + pc.slice_size = params.slice_size; + pc.block_size = params.block_size; + pc.x_shift = params.x_shift; + pc.block_height = params.block_height; + pc.block_height_mask = params.block_height_mask; + pc.block_depth = params.block_depth; + pc.block_depth_mask = params.block_depth_mask; + + scheduler.RequestOutsideRenderPassOperationContext(); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset, + image.guest_size_bytes - sw.buffer_offset); + compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); + + const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); + const VkDescriptorSet set = descriptor_allocator.Commit(); + + const u32 gx = Common::DivCeil(blocks_x, 8u); + const u32 gy = Common::DivCeil(blocks_y, 8u); + const u32 gz = Common::DivCeil(blocks_z, 4u); + const bool is_initialized = image.ExchangeInitialization(); + + const VkBuffer out_buffer = output.buffer; + const VkDeviceSize out_offset = output.offset; + const VkImage dst_image = image.Handle(); + const VkImageAspectFlags aspect = image.AspectMask(); + const VkExtent3D extent{ + .width = image.info.size.width, + .height = image.info.size.height, + .depth = image.info.size.depth, + }; + + scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer, + out_offset, dst_image, aspect, extent, + is_initialized](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { + return; + } + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); + cmdbuf.Dispatch(gx, gy, gz); + + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = out_offset, + .size = output_size, + }; + const VkImageMemoryBarrier pre_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT + : VK_ACCESS_NONE), + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE + : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + + const VkBufferImageCopy copy{ + .bufferOffset = out_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = aspect, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = extent, + }; + cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + + const VkImageMemoryBarrier post_copy{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{ + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, {}, {}, post_copy); + }); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 3d30aa6bdc..5150e95b90 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -164,4 +164,42 @@ private: ComputePassDescriptorQueue& compute_pass_descriptor_queue; }; +class BlockLinearUnswizzle2DPass final : public ComputePass { +public: + explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~BlockLinearUnswizzle2DPass(); + + [[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info); + + void Unswizzle(Image& image, const StagingBufferRef& swizzled, + std::span swizzles); + +private: + Scheduler& scheduler; + StagingBufferPool& staging_buffer_pool; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + +class BlockLinearUnswizzle3DBufferPass final : public ComputePass { +public: + explicit BlockLinearUnswizzle3DBufferPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~BlockLinearUnswizzle3DBufferPass(); + + [[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info); + + void Unswizzle(Image& image, const StagingBufferRef& swizzled, + std::span swizzles); + +private: + Scheduler& scheduler; + StagingBufferPool& staging_buffer_pool; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index bd3117037f..db9c5bf497 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -933,6 +933,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue); } + bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, + compute_pass_descriptor_queue); + bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, + compute_pass_descriptor_queue); } void TextureCacheRuntime::Finish() { @@ -1796,6 +1800,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu } flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::CostlyLoad; + } else if (runtime->bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(info)) { + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; + flags |= VideoCommon::ImageFlagBits::CostlyLoad; + } else if (runtime->bl3db_unswizzle_pass && + BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) { + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; + flags |= VideoCommon::ImageFlagBits::CostlyLoad; } if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) { flags |= VideoCommon::ImageFlagBits::Converted; @@ -2843,6 +2854,15 @@ void TextureCacheRuntime::AccelerateImageUpload( return astc_decoder_pass->Assemble(image, map, swizzles); } + if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(image.info)) { + return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles); + } + + if (bl3db_unswizzle_pass && + BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) { + return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles); + } + if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 4b4900e5d6..f183cc160a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -149,6 +149,8 @@ public: std::optional astc_decoder_pass; std::optional bl3d_unswizzle_pass; + std::optional bl2d_unswizzle_pass; + std::optional bl3db_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats;