From 8de624b63250d10be15d3314516c4ce671133baa Mon Sep 17 00:00:00 2001 From: Forrest Mark X Date: Tue, 7 Jul 2026 18:03:47 -0500 Subject: [PATCH] Attempt to improve the GPU unswizzle speed --- .../block_linear_unswizzle_3d_bcn.comp | 20 ++- .../renderer_vulkan/vk_compute_pass.cpp | 170 +++++++----------- .../renderer_vulkan/vk_compute_pass.h | 16 +- 3 files changed, 83 insertions(+), 123 deletions(-) diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp index e084837f25..3b91033a88 100644 --- a/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp @@ -75,6 +75,12 @@ layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { uint out_u32[]; }; +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU64 { + uvec2 out_u64[]; +}; +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU128 { + uvec4 out_u128[]; +}; // --- Constants --- layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in; @@ -153,12 +159,14 @@ void main() { uint block_index = block_coord.x + (block_coord.y * pc.blocks_dim.x) + (block_coord.z * pc.blocks_dim.x * pc.blocks_dim.y); - uint out_idx = block_index * (bytes_per_block >> 2u); - out_u32[out_idx] = texel.x; - out_u32[out_idx + 1u] = texel.y; - if (pc.bytes_per_block_log2 == 4u) { - out_u32[out_idx + 2u] = texel.z; - out_u32[out_idx + 3u] = texel.w; + if (pc.bytes_per_block_log2 == 4u) { // 16 bytes (BC3/BC7) + out_u128[block_index] = texel; + } else if (pc.bytes_per_block_log2 == 3u) { // 8 bytes (BC1/BC4) + out_u64[block_index] = texel.xy; + } else { + uint out_idx = block_index * (bytes_per_block >> 2u); + out_u32[out_idx] = texel.x; + if ((bytes_per_block >> 2u) > 1u) out_u32[out_idx + 1u] = texel.y; } } \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index b7781ef81b..54796ca1ac 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -725,15 +725,38 @@ void BlockLinearUnswizzle3DPass::Unswizzle( : VK_IMAGE_LAYOUT_UNDEFINED; scheduler.RequestOutsideRenderPassOperationContext(); + + scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask(), initial_prior_layout](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE) { + return; + } + + const VkAccessFlags src_access = initial_prior_layout == VK_IMAGE_LAYOUT_GENERAL + ? (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT) : 0u; + const VkPipelineStageFlags src_stage = initial_prior_layout == VK_IMAGE_LAYOUT_GENERAL + ? (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT) : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + const VkImageMemoryBarrier pre_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = initial_prior_layout, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange = {aspect, 0, 1, 0, 1}, + }; + + cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, pre_barrier); + }); + for (u32 z_offset = 0; z_offset < z_count; z_offset += MAX_BATCH_SLICES) { const u32 current_chunk_slices = (std::min)(MAX_BATCH_SLICES, z_count - z_offset); const u32 current_z_src = z_src_start + z_offset; const u32 current_z_dst = z_image_start + z_offset; - const VkImageLayout prior_layout = (z_offset == 0) - ? initial_prior_layout - : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - bool chunk_has_data = slice_has_data.empty(); if (!chunk_has_data) { const u32 z_src_end = current_z_src + current_chunk_slices; @@ -747,12 +770,33 @@ void BlockLinearUnswizzle3DPass::Unswizzle( if (chunk_has_data) { UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, - current_z_src, current_z_dst, current_chunk_slices, prior_layout); + current_z_src, current_z_dst, current_chunk_slices); } else { UnswizzleZeroChunk(image, blocks_x, blocks_y, bytes_per_block, - current_z_dst, current_chunk_slices, prior_layout); + current_z_dst, current_chunk_slices); } } + + scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask()](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE) return; + + const VkImageMemoryBarrier post_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_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 = {aspect, 0, 1, 0, 1}, + }; + + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, post_barrier); + }); } void BlockLinearUnswizzle3DPass::UnswizzleChunk( @@ -761,10 +805,10 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const VideoCommon::SwizzleParameters& sw, const BlockLinearSwizzle3DParams& params, u32 blocks_x, u32 blocks_y, - u32 z_src, u32 z_dst, u32 z_count, - VkImageLayout prior_image_layout) + u32 z_src, u32 z_dst, u32 z_count) { BlockLinearUnswizzle3DPushConstants pc{}; + pc.origin[0] = params.origin[0]; pc.origin[1] = params.origin[1]; pc.origin[2] = z_src; // Current chunk's Z start @@ -789,7 +833,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( compute_pass_descriptor_queue.Acquire(scheduler, 3); compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset, - image.guest_size_bytes - sw.buffer_offset); + VK_WHOLE_SIZE); compute_pass_descriptor_queue.AddBuffer(*image.compute_unswizzle_buffer, 0, image.compute_unswizzle_buffer_size); @@ -805,13 +849,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( static_cast(blocks_x) * blocks_y * bytes_per_block; const VkDeviceSize barrier_size = output_slice_size * z_count; - const VkAccessFlags src_access = - (prior_image_layout == VK_IMAGE_LAYOUT_UNDEFINED) - ? VkAccessFlags{} - : (prior_image_layout == VK_IMAGE_LAYOUT_GENERAL) - ? static_cast(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT) - : static_cast(VK_ACCESS_TRANSFER_WRITE_BIT); - const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkImage dst_image = image.Handle(); const VkImageAspectFlags aspect = image.AspectMask(); @@ -820,10 +857,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, z_dst, z_count, barrier_size, - prior_image_layout, src_access, out_buffer, dst_image, aspect, - image_width, image_height - ](vk::CommandBuffer cmdbuf) { + image_width, image_height](vk::CommandBuffer cmdbuf) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { return; @@ -848,26 +883,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .size = barrier_size, }; - // Image layout transition - const VkImageMemoryBarrier pre_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = src_access, - .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = prior_image_layout, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst_image, - .subresourceRange = {aspect, 0, 1, 0, 1}, - }; - - // Single barrier handles both buffer and image - cmdbuf.PipelineBarrier( - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, nullptr, buffer_barrier, pre_barrier - ); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, buffer_barrier); // Copy chunk to correct Z position in image const VkBufferImageCopy copy{ @@ -880,27 +896,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( }; cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); - - // Post-copy transition - const VkImageMemoryBarrier post_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_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 = {aspect, 0, 1, 0, 1}, - }; - - cmdbuf.PipelineBarrier( - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, - nullptr, nullptr, post_barrier - ); }); } @@ -1006,8 +1001,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( Image& image, u32 blocks_x, u32 blocks_y, u32 bytes_per_block, - u32 z_dst, u32 z_count, - VkImageLayout prior_image_layout) + u32 z_dst, u32 z_count) { ASSERT(image.has_compute_unswizzle_buffer); @@ -1024,17 +1018,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( static_cast(blocks_x) * blocks_y * bytes_per_block; const VkDeviceSize fill_size = output_slice_bytes * z_count; - const VkAccessFlags src_access = - (prior_image_layout == VK_IMAGE_LAYOUT_UNDEFINED) - ? VkAccessFlags{} - : (prior_image_layout == VK_IMAGE_LAYOUT_GENERAL) - ? static_cast(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT) - : static_cast(VK_ACCESS_TRANSFER_WRITE_BIT); - scheduler.Record([out_buffer, dst_image, aspect, z_dst, z_count, - fill_size, prior_image_layout, src_access, - image_width, image_height - ](vk::CommandBuffer cmdbuf) { + fill_size, image_width, image_height](vk::CommandBuffer cmdbuf) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { return; @@ -1052,25 +1037,13 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = out_buffer, .offset = 0, - .size = fill_size, - }; - const VkImageMemoryBarrier pre_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = src_access, - .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = prior_image_layout, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst_image, - .subresourceRange = {aspect, 0, 1, 0, 1}, + .size = fill_size, }; + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, nullptr, buffer_barrier, pre_barrier - ); + 0, buffer_barrier); // Copy the zeroed buffer region into the correct Z position of the image. const VkBufferImageCopy copy{ @@ -1082,26 +1055,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( .imageExtent = {image_width, image_height, z_count}, }; cmdbuf.CopyBufferToImage(out_buffer, dst_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); - - // Transition image to GENERAL for subsequent shader reads/writes. - const VkImageMemoryBarrier post_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_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 = {aspect, 0, 1, 0, 1}, - }; - cmdbuf.PipelineBarrier( - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, nullptr, nullptr, post_barrier - ); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); }); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 92003dc229..fe2cca386d 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -153,20 +153,18 @@ public: bool image_already_uploaded); void UnswizzleChunk( - Image& image, - const StagingBufferRef& swizzled, - const VideoCommon::SwizzleParameters& sw, - const BlockLinearSwizzle3DParams& params, + Image &image, + const StagingBufferRef &swizzled, + const VideoCommon::SwizzleParameters &sw, + const BlockLinearSwizzle3DParams ¶ms, u32 blocks_x, u32 blocks_y, - u32 z_src, u32 z_dst, u32 z_count, - VkImageLayout prior_image_layout); + u32 z_src, u32 z_dst, u32 z_count); void UnswizzleZeroChunk( - Image& image, + Image &image, u32 blocks_x, u32 blocks_y, u32 bytes_per_block, - u32 z_dst, u32 z_count, - VkImageLayout prior_image_layout); + u32 z_dst, u32 z_count); private: Scheduler& scheduler;