From b40b9ecdef86d4b75c53b4c4dc79987a6151d825 Mon Sep 17 00:00:00 2001 From: Forrest Mark X Date: Sun, 12 Jul 2026 04:53:28 -0500 Subject: [PATCH] Added boundingbox generation for sparse textures Should speed up sprase texture handling more as it should only process the data that actually exists within the sparse texture --- .../block_linear_unswizzle_3d_bcn.comp | 39 +--- .../renderer_opengl/gl_texture_cache.cpp | 4 +- .../renderer_opengl/gl_texture_cache.h | 11 +- .../renderer_vulkan/vk_compute_pass.cpp | 193 ++++++++++++------ .../renderer_vulkan/vk_compute_pass.h | 6 +- .../renderer_vulkan/vk_texture_cache.cpp | 5 +- .../renderer_vulkan/vk_texture_cache.h | 10 +- .../texture_cache/accelerated_swizzle.cpp | 55 +++++ .../texture_cache/accelerated_swizzle.h | 10 + src/video_core/texture_cache/texture_cache.h | 28 +++ .../texture_cache/texture_cache_base.h | 3 + 11 files changed, 255 insertions(+), 109 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 3b91033a88..17fcbb8c46 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 @@ -4,21 +4,12 @@ #version 430 #ifdef VULKAN - #extension GL_EXT_shader_16bit_storage : require - #extension GL_EXT_shader_8bit_storage : require - #define HAS_EXTENDED_TYPES 1 #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define END_PUSH_CONSTANTS }; #define UNIFORM(n) #define BINDING_INPUT_BUFFER 0 #define BINDING_OUTPUT_BUFFER 1 #else - #extension GL_NV_gpu_shader5 : enable - #ifdef GL_NV_gpu_shader5 - #define HAS_EXTENDED_TYPES 1 - #else - #define HAS_EXTENDED_TYPES 0 - #endif #define BEGIN_PUSH_CONSTANTS #define END_PUSH_CONSTANTS #define UNIFORM(n) layout(location = n) uniform @@ -64,17 +55,9 @@ END_PUSH_CONSTANTS #endif // --- Buffers --- -#if HAS_EXTENDED_TYPES -layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; -layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; -#endif -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(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU64 { uvec2 out_u64[]; }; @@ -113,20 +96,10 @@ uint SwizzleOffset(uvec2 pos) { } uvec4 ReadTexel(uint offset) { - uint bpl2 = pc.bytes_per_block_log2; - switch (bpl2) { -#if HAS_EXTENDED_TYPES - case 0u: return uvec4(u8data[offset], 0u, 0u, 0u); - case 1u: return uvec4(u16data[offset / 2u], 0u, 0u, 0u); -#else - case 0u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 24u), 8), 0u, 0u, 0u); - case 1u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 16u), 16), 0u, 0u, 0u); -#endif - 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]; + if (pc.bytes_per_block_log2 == 4u) { + return u128data[offset / 16u]; } - return uvec4(0u); + return uvec4(u64data[offset / 8u], 0u, 0u); } void main() { @@ -162,11 +135,7 @@ void main() { 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) + } else { // 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_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 669fa4685f..22676cd8ee 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -650,11 +650,11 @@ void TextureCacheRuntime::BlitFramebuffer(Framebuffer* dst, Framebuffer* src, is_linear ? GL_LINEAR : GL_NEAREST); } -void TextureCacheRuntime::AccelerateImageUpload(Image& image, const StagingBufferMap& map, +void TextureCacheRuntime::AccelerateImageUpload(Image &image, const StagingBufferMap &map, std::span swizzles, u32 z_src_start, u32 z_image_start, u32 z_count, [[maybe_unused]] std::span slice_has_data, - [[maybe_unused]] bool image_already_uploaded) { + std::span slice_bounds, [[maybe_unused]] bool image_already_uploaded) { switch (image.info.type) { case ImageType::e2D: if (IsPixelFormatASTC(image.info.format)) { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 376f219abb..fa68a14438 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -118,11 +118,12 @@ public: const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, Tegra::Engines::Fermi2D::Operation operation); - void AccelerateImageUpload(Image& image, const StagingBufferMap& map, - std::span swizzles, - u32 z_src_start, u32 z_image_start, u32 z_count, - std::span slice_has_data = {}, - bool image_already_uploaded = false); + void AccelerateImageUpload(Image&, const StagingBufferMap&, + std::span, + u32 z_src_start, u32 z_image_start, u32 z_count, + std::span slice_has_data = {}, + std::span slice_bounds = {}, + bool image_already_uploaded = false); void InsertUploadMemoryBarrier(); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 54796ca1ac..8c5634fb9e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -703,6 +703,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle( std::span swizzles, u32 z_src_start, u32 z_image_start, u32 z_count, std::span slice_has_data, + std::span slice_bounds, bool image_already_uploaded) { using namespace VideoCommon::Accelerated; @@ -718,7 +719,6 @@ void BlockLinearUnswizzle3DPass::Unswizzle( const u32 blocks_x = (image.info.size.width + 3) / 4; const u32 blocks_y = (image.info.size.height + 3) / 4; - const u32 bytes_per_block = 1u << params.bytes_per_block_log2; const VkImageLayout initial_prior_layout = image_already_uploaded ? VK_IMAGE_LAYOUT_GENERAL @@ -752,28 +752,99 @@ void BlockLinearUnswizzle3DPass::Unswizzle( 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; - - bool chunk_has_data = slice_has_data.empty(); - if (!chunk_has_data) { - const u32 z_src_end = current_z_src + current_chunk_slices; - for (u32 z = current_z_src; z < z_src_end; ++z) { - if (z < static_cast(slice_has_data.size()) && slice_has_data[z] != 0) { - chunk_has_data = true; - break; - } + struct Run { bool has_data; u32 start; u32 len; }; + boost::container::small_vector runs; + { + u32 pos = 0; + while (pos < z_count) { + const u32 src = z_src_start + pos; + const bool val = slice_has_data.empty() || + (src < static_cast(slice_has_data.size()) && slice_has_data[src] != 0); + u32 len = 1; + while (pos + len < z_count) { + const u32 next_src = z_src_start + pos + len; + const bool next_val = slice_has_data.empty() || + (next_src < static_cast(slice_has_data.size()) && slice_has_data[next_src] != 0); + if (next_val != val) break; + ++len; } + runs.push_back({val, pos, len}); + pos += len; } + } - if (chunk_has_data) { - UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, - current_z_src, current_z_dst, current_chunk_slices); + static constexpr u32 MAX_RUNS_PER_BATCH = 8; + const u32 min_run_len = (std::max)(4u, z_count / MAX_RUNS_PER_BATCH); + for (Run& r : runs) { + if (!r.has_data && r.len < min_run_len) { + r.has_data = true; + } + } + boost::container::small_vector merged; + for (const Run& r : runs) { + if (!merged.empty() && merged.back().has_data == r.has_data) { + merged.back().len += r.len; } else { - UnswizzleZeroChunk(image, blocks_x, blocks_y, bytes_per_block, - current_z_dst, current_chunk_slices); + merged.push_back(r); + } + } + + for (const Run& r : merged) { + u32 sub_offset = 0; + while (sub_offset < r.len) { + const u32 sub_len = (std::min)(r.len - sub_offset, MAX_BATCH_SLICES); + const u32 z_src = z_src_start + r.start + sub_offset; + const u32 z_dst = z_image_start + r.start + sub_offset; + + if (!r.has_data) { + UnswizzleZeroChunk(image, z_dst, sub_len); + sub_offset += sub_len; + continue; + } + + u32 ox0 = 0, oy0 = 0, ox1 = blocks_x, oy1 = blocks_y; + if (!slice_bounds.empty()) { + bool any = false; + u32 ux0 = blocks_x, uy0 = blocks_y, ux1 = 0, uy1 = 0; + for (u32 z = z_src; z < z_src + sub_len; ++z) { + if (z >= static_cast(slice_bounds.size())) { any = false; break; } + const auto& b = slice_bounds[z]; + if (b.x1 <= b.x0 || b.y1 <= b.y0) continue; + ux0 = (std::min)(ux0, b.x0); + uy0 = (std::min)(uy0, b.y0); + ux1 = (std::max)(ux1, b.x1); + uy1 = (std::max)(uy1, b.y1); + any = true; + } + if (any) { ox0 = ux0; oy0 = uy0; ox1 = ux1; oy1 = uy1; } + } + + // Uncomment if garbage data starts appearing + //UnswizzleZeroChunk(image, z_dst, sub_len); + + scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask()](vk::CommandBuffer cmdbuf) { + if (dst_image == VK_NULL_HANDLE) return; + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .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(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, barrier); + }); + + UnswizzleChunk(image, swizzled, sw, params, + ox0, oy0, ox1 - ox0, oy1 - oy0, + z_src, z_dst, sub_len); + + sub_offset += sub_len; } } @@ -804,13 +875,16 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( const StagingBufferRef& swizzled, const VideoCommon::SwizzleParameters& sw, const BlockLinearSwizzle3DParams& params, - u32 blocks_x, u32 blocks_y, + u32 origin_x, u32 origin_y, + u32 extent_x, u32 extent_y, u32 z_src, u32 z_dst, u32 z_count) { + image.compute_unswizzle_buffer_is_zero = false; + BlockLinearUnswizzle3DPushConstants pc{}; - pc.origin[0] = params.origin[0]; - pc.origin[1] = params.origin[1]; + pc.origin[0] = params.origin[0] + origin_x * 4u; + pc.origin[1] = params.origin[1] + origin_y * 4u; pc.origin[2] = z_src; // Current chunk's Z start pc.destination[0] = params.destination[0]; @@ -826,8 +900,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( pc.block_depth = params.block_depth; pc.block_depth_mask = params.block_depth_mask; - pc.blocks_dim[0] = blocks_x; - pc.blocks_dim[1] = blocks_y; + pc.blocks_dim[0] = extent_x; + pc.blocks_dim[1] = extent_y; pc.blocks_dim[2] = z_count; // Only process the count compute_pass_descriptor_queue.Acquire(scheduler, 3); @@ -840,25 +914,27 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( 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 gx = Common::DivCeil(extent_x, 8u); + const u32 gy = Common::DivCeil(extent_y, 8u); const u32 gz = Common::DivCeil(z_count, 4u); const u32 bytes_per_block = 1u << pc.bytes_per_block_log2; const VkDeviceSize output_slice_size = - static_cast(blocks_x) * blocks_y * bytes_per_block; + static_cast(extent_x) * extent_y * bytes_per_block; const VkDeviceSize barrier_size = output_slice_size * z_count; const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkImage dst_image = image.Handle(); const VkImageAspectFlags aspect = image.AspectMask(); - const u32 image_width = image.info.size.width; - const u32 image_height = image.info.size.height; + const s32 dst_x = static_cast(origin_x * 4u); + const s32 dst_y = static_cast(origin_y * 4u); + const u32 copy_width = extent_x * 4u; + const u32 copy_height = extent_y * 4u; scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, z_dst, z_count, barrier_size, out_buffer, dst_image, aspect, - image_width, image_height](vk::CommandBuffer cmdbuf) { + dst_x, dst_y, copy_width, copy_height](vk::CommandBuffer cmdbuf) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { return; @@ -882,7 +958,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .offset = 0, .size = barrier_size, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, buffer_barrier); // Copy chunk to correct Z position in image @@ -891,8 +966,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( .bufferRowLength = 0, .bufferImageHeight = 0, .imageSubresource = {aspect, 0, 0, 1}, - .imageOffset = {0, 0, static_cast(z_dst)}, // Write to correct Z - .imageExtent = {image_width, image_height, z_count}, + .imageOffset = {dst_x, dst_y, static_cast(z_dst)}, + .imageExtent = {copy_width, copy_height, z_count}, }; cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); @@ -999,8 +1074,6 @@ void MSAACopyPass::CopyImage(Image& dst_image, Image& src_image, // So enjoy this mess void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( Image& image, - u32 blocks_x, u32 blocks_y, - u32 bytes_per_block, u32 z_dst, u32 z_count) { ASSERT(image.has_compute_unswizzle_buffer); @@ -1010,40 +1083,42 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( const VkImageAspectFlags aspect = image.AspectMask(); const u32 image_width = image.info.size.width; const u32 image_height = image.info.size.height; + const bool needs_fill = !image.compute_unswizzle_buffer_is_zero; + const VkDeviceSize buffer_capacity = image.compute_unswizzle_buffer_size; - // Size of one unswizzled z-slice in the output buffer (bytes). - // bytes_per_block was removed here at one point but caused graphics corruption which makes sense as this is processing DXT1-7 textures and without it I'll be initilizing a buffer that is far far smaller than the actual texture - // I can look more into this later if at some point I want this to work with non-DXT textures - const VkDeviceSize output_slice_bytes = - static_cast(blocks_x) * blocks_y * bytes_per_block; - const VkDeviceSize fill_size = output_slice_bytes * z_count; + if (needs_fill) { + image.compute_unswizzle_buffer_is_zero = true; + } scheduler.Record([out_buffer, dst_image, aspect, z_dst, z_count, - fill_size, image_width, image_height](vk::CommandBuffer cmdbuf) { + buffer_capacity, needs_fill, + image_width, image_height](vk::CommandBuffer cmdbuf) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { return; } - // Zero the output buffer region that CopyBufferToImage will read. - cmdbuf.FillBuffer(out_buffer, 0, fill_size, 0u); + if (needs_fill) { + // Zero the output buffer region that CopyBufferToImage will read. + cmdbuf.FillBuffer(out_buffer, 0, buffer_capacity, 0u); - const VkBufferMemoryBarrier buffer_barrier{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = out_buffer, - .offset = 0, - .size = fill_size, - }; + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = 0, + .size = buffer_capacity, + }; - cmdbuf.PipelineBarrier( - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, buffer_barrier); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, buffer_barrier); + } // Copy the zeroed buffer region into the correct Z position of the image. const VkBufferImageCopy copy{ diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index fe2cca386d..b92f03c86e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -150,6 +150,7 @@ public: std::span swizzles, u32 z_src_start, u32 z_image_start, u32 z_count, std::span slice_has_data, + std::span slice_bounds, bool image_already_uploaded); void UnswizzleChunk( @@ -157,13 +158,12 @@ public: const StagingBufferRef &swizzled, const VideoCommon::SwizzleParameters &sw, const BlockLinearSwizzle3DParams ¶ms, - u32 blocks_x, u32 blocks_y, + u32 origin_x, u32 origin_y, + u32 extent_x, u32 extent_y, u32 z_src, u32 z_dst, u32 z_count); void UnswizzleZeroChunk( Image &image, - u32 blocks_x, u32 blocks_y, - u32 bytes_per_block, u32 z_dst, u32 z_count); private: diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 517c7bdb25..0fe43466e3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1869,6 +1869,7 @@ void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) { runtime->memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal); has_compute_unswizzle_buffer = true; + compute_unswizzle_buffer_is_zero = false; } void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, @@ -2839,6 +2840,7 @@ void TextureCacheRuntime::AccelerateImageUpload( std::span swizzles, u32 z_src_start, u32 z_image_start, u32 z_count, std::span slice_has_data, + std::span slice_bounds, bool image_already_uploaded) { if (IsPixelFormatASTC(image.info.format)) { @@ -2861,7 +2863,8 @@ void TextureCacheRuntime::AccelerateImageUpload( return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_src_start, z_image_start, z_count, - slice_has_data, image_already_uploaded); + slice_has_data, slice_bounds, + image_already_uploaded); } ASSERT(false); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 1e806918fd..0f1422c350 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -92,10 +92,11 @@ public: } void AccelerateImageUpload(Image&, const StagingBufferRef&, - std::span, - u32 z_src_start, u32 z_image_start, u32 z_count, - std::span slice_has_data = {}, - bool image_already_uploaded = false); + std::span, + u32 z_src_start, u32 z_image_start, u32 z_count, + std::span slice_has_data = {}, + std::span slice_bounds = {}, + bool image_already_uploaded = false); void InsertUploadMemoryBarrier() {} @@ -341,6 +342,7 @@ private: vk::Buffer compute_unswizzle_buffer; VkDeviceSize compute_unswizzle_buffer_size = 0; bool has_compute_unswizzle_buffer = false; + bool compute_unswizzle_buffer_is_zero = false; void AllocateComputeUnswizzleBuffer(u32 max_slices); diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp index 4c3f724d79..b4f4ea3b08 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.cpp +++ b/src/video_core/texture_cache/accelerated_swizzle.cpp @@ -20,6 +20,9 @@ using Tegra::Texture::GOB_SIZE_X_SHIFT; using Tegra::Texture::GOB_SIZE_Y_SHIFT; using VideoCore::Surface::BytesPerBlock; +constexpr u32 GOB_SIZE_Y = 8; +constexpr u32 GOB_SIZE = 512; + BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameters& swizzle, const ImageInfo& info) { const Extent3D block = swizzle.block; @@ -66,4 +69,56 @@ BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameter }; } +SliceBBox BoundSliceByteRange(u64 start_off, u64 end_off, + u32 block_size, u32 x_shift, + u32 block_height, u32 block_height_mask, + u32 bytes_per_block, + u32 blocks_x, u32 blocks_y) { + if (start_off >= end_off) { + return {}; + } + + const u32 column_pitch = 1u << x_shift; + const u32 blocks_per_column = 64u / bytes_per_block; + const u32 rows_per_group = 1u << block_height; + const u32 y_per_band = GOB_SIZE_Y << block_height; + + const u64 band_start = start_off / block_size; + const u64 band_end = (end_off - 1) / block_size; + + SliceBBox box; + + if (band_start == band_end) { + const u64 wb_start = start_off - band_start * block_size; + const u64 wb_end = (end_off - 1) - band_start * block_size; + + const u32 col_start = static_cast(wb_start / column_pitch); + const u32 col_end = static_cast(wb_end / column_pitch); + + if (col_start == col_end) { + const u32 row_start = static_cast(wb_start % column_pitch) / GOB_SIZE; + const u32 row_end = static_cast(wb_end % column_pitch) / GOB_SIZE; + + box.x0 = col_start * blocks_per_column; + box.x1 = (col_start + 1) * blocks_per_column; + box.y0 = (static_cast(band_start) * rows_per_group + row_start) * GOB_SIZE_Y; + box.y1 = (static_cast(band_start) * rows_per_group + row_end + 1) * GOB_SIZE_Y; + } else { + box.x0 = col_start * blocks_per_column; + box.x1 = (col_end + 1) * blocks_per_column; + box.y0 = static_cast(band_start) * y_per_band; + box.y1 = box.y0 + y_per_band; + } + } else { + box.x0 = 0; + box.x1 = blocks_x; + box.y0 = static_cast(band_start) * y_per_band; + box.y1 = static_cast(band_end + 1) * y_per_band; + } + + box.x1 = (std::min)(box.x1, blocks_x); + box.y1 = (std::min)(box.y1, blocks_y); + return box; +} + } // namespace VideoCommon::Accelerated diff --git a/src/video_core/texture_cache/accelerated_swizzle.h b/src/video_core/texture_cache/accelerated_swizzle.h index d4250da689..32b18f3c89 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.h +++ b/src/video_core/texture_cache/accelerated_swizzle.h @@ -35,10 +35,20 @@ struct BlockLinearSwizzle3DParams { u32 block_depth_mask; }; +struct SliceBBox { + u32 x0 = 0, y0 = 0, x1 = 0, y1 = 0; +}; + [[nodiscard]] BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams( const SwizzleParameters& swizzle, const ImageInfo& info); [[nodiscard]] BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams( const SwizzleParameters& swizzle, const ImageInfo& info); +[[nodiscard]] SliceBBox BoundSliceByteRange(u64 start_off, u64 end_off, + u32 block_size, u32 x_shift, + u32 block_height, u32 block_height_mask, + u32 bytes_per_block, + u32 blocks_x, u32 blocks_y); + } // namespace VideoCommon::Accelerated diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index ca5082b1f1..7581af0f39 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1430,6 +1430,7 @@ void TextureCache

::TickAsyncUnswizzle() { task.segment_scan_cursor = 0; task.slice_has_data.assign(image.info.size.depth, 0u); + task.slice_bounds.assign(image.info.size.depth, {}); if (image.info.size.depth > 1 && !image.slice_offsets.empty()) { const auto uploads = FullUploadSwizzles(task.info); @@ -1439,6 +1440,10 @@ void TextureCache

::TickAsyncUnswizzle() { task.swizzled_slice_size = swizzled_slice_size; task.swizzle_block_depth = sp.block_depth; + const bool can_bound_xy = sp.block_depth == 0; + const u32 blocks_x = Common::DivCeil(task.info.size.width, 4u); + const u32 blocks_y = Common::DivCeil(task.info.size.height, 4u); + u32 z_watermark = 0; for (const auto& [seg_gpu_addr, seg_size] : task.sparse_segments) { const u64 seg_start = seg_gpu_addr - image.gpu_addr; @@ -1451,6 +1456,28 @@ void TextureCache

::TickAsyncUnswizzle() { for (u32 z = z_watermark; z < static_cast(image.info.size.depth); ++z) { if (image.slice_offsets[z] >= seg_end) break; task.slice_has_data[z] = 1u; + + if (can_bound_xy) { + const u64 slice_off = image.slice_offsets[z]; + const u64 local_start = (std::max)(seg_start, slice_off) - slice_off; + const u64 local_end = (std::min)(seg_end, slice_off + swizzled_slice_size) - slice_off; + const auto box = VideoCommon::Accelerated::BoundSliceByteRange( + local_start, local_end, sp.block_size, sp.x_shift, + sp.block_height, sp.block_height_mask, bytes_per_block, + blocks_x, blocks_y); + + auto& acc = task.slice_bounds[z]; + if (box.x1 > box.x0 && box.y1 > box.y0) { + if (acc.x1 <= acc.x0 || acc.y1 <= acc.y0) { + acc = box; + } else { + acc.x0 = (std::min)(acc.x0, box.x0); + acc.y0 = (std::min)(acc.y0, box.y0); + acc.x1 = (std::max)(acc.x1, box.x1); + acc.y1 = (std::max)(acc.y1, box.y1); + } + } + } } } } else { @@ -1577,6 +1604,7 @@ void TextureCache

::TickAsyncUnswizzle() { FixSmallVectorADL(uploads), z_src, z_image, z_count, sparse_hint, + std::span(task.slice_bounds), task.is_incremental); task.last_submitted_offset += static_cast(z_count) * task.bytes_per_slice; } diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 36651f7859..162f483192 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -25,6 +25,8 @@ #include "common/literals.h" #include "common/lru_cache.h" #include + +#include "accelerated_swizzle.h" #include "common/scratch_buffer.h" #include "common/slot_vector.h" #include "common/thread_worker.h" @@ -141,6 +143,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches slice_has_data; + std::vector slice_bounds; std::vector> sparse_segments; size_t segment_scan_cursor = 0; u64 swizzled_slice_size = 0;