From 6874cfdc605bc5b4b9175055a9544345836599bc Mon Sep 17 00:00:00 2001 From: Forrest Mark X Date: Mon, 10 Aug 2026 13:57:15 -0500 Subject: [PATCH] Fixed GPU async unswizzle Fixed typo in unswizzle method name Added experimental change to try and vastly speed up the GPU async processing without degrading too much performance --- src/qt_common/config/shared_translation.cpp | 2 +- src/video_core/texture_cache/texture_cache.h | 126 ++++++++++-------- .../texture_cache/texture_cache_base.h | 1 + 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index acf16eafaa..b0d4fe794e 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -169,7 +169,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) { tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " "decoding, or perform no decoding at all (black screen on videos).\n" "In most cases, GPU decoding provides the best performance.")); - INSERT(Settings, accelerate_unswizzle, tr("Texture Unsiwzzle Method:"), + INSERT(Settings, accelerate_unswizzle, tr("Texture Unswizzle Method:"), tr("This option controls how generic textures should be unswizzled.\n" "CPU: Use the CPU for unswizzling (recommended).\n" "GPU: Use the GPU's compute shaders to unswizzling generic textures.")); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 69e2a68670..20ab473f93 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1412,8 +1412,6 @@ void TextureCache

::TickAsyncUnswizzle() { ImageId task_image_id = task.image_id; if (task.is_cpu) { - // The primary resource drain here is UnswizzleTexture so maybe this won't blow up your SteamDeck - // Also, scary infinite loop possibility ... makes me uncomfortable while (true) { PendingUnswizzle& current = unswizzle_queue.front(); Image& image = slot_images[task_image_id]; @@ -1440,8 +1438,27 @@ void TextureCache

::TickAsyncUnswizzle() { } } } else { - Image& image = slot_images[task_image_id]; - TickAsyncUnswizzleGpu(task, image); + while (true) { + PendingUnswizzle& current = unswizzle_queue.front(); + Image& image = slot_images[task_image_id]; + const size_t offset_before = current.current_offset; + + TickAsyncUnswizzleGpu(current, image); + + if (unswizzle_queue.empty() || unswizzle_queue.front().image_id != task_image_id) { + break; + } + + PendingUnswizzle& refreshed = unswizzle_queue.front(); + if (refreshed.current_offset == offset_before) { + break; + } + + if (!refreshed.owns_staging_buffer && unswizzle_shared_staging_pending_gpu_read) { + runtime.Finish(); + unswizzle_shared_staging_pending_gpu_read = false; + } + } } } @@ -1576,6 +1593,7 @@ void TextureCache

::TickAsyncUnswizzleGpu(PendingUnswizzle& task, Image& image task.last_submitted_offset = static_cast(task.active_z_start) * task.bytes_per_slice; } + task.current_batch_start_byte = task.current_offset; } const size_t needed = (std::max)(max_batch_size, size_t{1}); @@ -1609,65 +1627,67 @@ void TextureCache

::TickAsyncUnswizzleGpu(PendingUnswizzle& task, Image& image task.initialized = true; } - // Read data - const size_t active_start_byte = (task.active_z_start < image.slice_offsets.size()) - ? static_cast(image.slice_offsets[task.active_z_start]) : 0; const size_t active_end_byte = (task.active_z_end < image.slice_offsets.size()) - ? static_cast(image.slice_offsets[task.active_z_end]) : task.total_size; + ? static_cast(image.slice_offsets[task.active_z_end]) : task.total_size; if (task.current_offset < active_end_byte) { - const size_t remaining = active_end_byte - task.current_offset; - size_t copy_amount = (async_unswizzle_chunk_size == 0) - ? remaining - : (std::min)(async_unswizzle_chunk_size, remaining); + const size_t batch_capacity_end_byte = (std::min)( + task.current_batch_start_byte + task.staging_buffer.mapped_span.size(), active_end_byte); - if (async_unswizzle_chunk_size > 0 && copy_amount < remaining) { - copy_amount = (copy_amount / task.bytes_per_slice) * task.bytes_per_slice; - if (copy_amount == 0) copy_amount = task.bytes_per_slice; - copy_amount = (std::min)(copy_amount, remaining); - } + if (task.current_offset < batch_capacity_end_byte) { + const size_t remaining = batch_capacity_end_byte - task.current_offset; + size_t copy_amount = (async_unswizzle_chunk_size == 0) + ? remaining + : (std::min)(async_unswizzle_chunk_size, remaining); - u8* const staging_base = task.staging_buffer.mapped_span.data(); + if (async_unswizzle_chunk_size > 0 && copy_amount < remaining) { + copy_amount = (copy_amount / task.bytes_per_slice) * task.bytes_per_slice; + if (copy_amount == 0) copy_amount = task.bytes_per_slice; + copy_amount = (std::min)(copy_amount, remaining); + } - if (task.is_sparse && task.current_offset == active_start_byte) { - const size_t window_size = active_end_byte - active_start_byte; - std::memset(staging_base, 0, (std::min)(window_size, task.staging_buffer.mapped_span.size())); - } + u8* const staging_base = task.staging_buffer.mapped_span.data(); - const size_t base_off = task.staging_base_byte_offset; - const size_t read_start = task.current_offset; - const size_t read_end = read_start + copy_amount; - const size_t abs_start = read_start + base_off; - const size_t abs_end = read_end + base_off; + if (task.is_sparse && task.current_offset == task.current_batch_start_byte) { + const size_t window_size = batch_capacity_end_byte - task.current_batch_start_byte; + std::memset(staging_base, 0, (std::min)(window_size, task.staging_buffer.mapped_span.size())); + } - if (task.is_sparse) { - while (task.segment_scan_cursor < task.sparse_segments.size()) { - const auto& [seg_gpu_addr, seg_size] = - task.sparse_segments[task.segment_scan_cursor]; - const size_t seg_abs_start = - static_cast(seg_gpu_addr - image.gpu_addr); - const size_t seg_abs_end = seg_abs_start + seg_size; + const size_t base_off = task.staging_base_byte_offset; + const size_t read_start = task.current_offset; + const size_t read_end = read_start + copy_amount; + const size_t abs_start = read_start + base_off; + const size_t abs_end = read_end + base_off; - if (seg_abs_end <= abs_start) { ++task.segment_scan_cursor; continue; } - if (seg_abs_start >= abs_end) { break; } + if (task.is_sparse) { + while (task.segment_scan_cursor < task.sparse_segments.size()) { + const auto& [seg_gpu_addr, seg_size] = + task.sparse_segments[task.segment_scan_cursor]; + const size_t seg_abs_start = + static_cast(seg_gpu_addr - image.gpu_addr); + const size_t seg_abs_end = seg_abs_start + seg_size; - const size_t ol_abs_start = (std::max)(seg_abs_start, abs_start); - const size_t ol_abs_end = (std::min)(seg_abs_end, abs_end); - const size_t ol_rel_start = (ol_abs_start - base_off) - active_start_byte; + if (seg_abs_end <= abs_start) { ++task.segment_scan_cursor; continue; } + if (seg_abs_start >= abs_end) { break; } - gpu_memory->ReadBlockUnsafe(image.gpu_addr + ol_abs_start, - staging_base + ol_rel_start, - ol_abs_end - ol_abs_start); + const size_t ol_abs_start = (std::max)(seg_abs_start, abs_start); + const size_t ol_abs_end = (std::min)(seg_abs_end, abs_end); + const size_t ol_rel_start = (ol_abs_start - base_off) - task.current_batch_start_byte; - if (seg_abs_end > abs_end) break; - ++task.segment_scan_cursor; + gpu_memory->ReadBlockUnsafe(image.gpu_addr + ol_abs_start, + staging_base + ol_rel_start, + ol_abs_end - ol_abs_start); + + if (seg_abs_end > abs_end) break; + ++task.segment_scan_cursor; + } + } else { + gpu_memory->ReadBlockUnsafe(image.gpu_addr + abs_start, + staging_base + (read_start - task.current_batch_start_byte), + copy_amount); } - } else { - gpu_memory->ReadBlockUnsafe(image.gpu_addr + abs_start, - staging_base + (read_start - active_start_byte), - copy_amount); + task.current_offset += copy_amount; } - task.current_offset += copy_amount; } const bool is_final_batch = task.current_offset >= active_end_byte; @@ -1702,6 +1722,7 @@ void TextureCache

::TickAsyncUnswizzleGpu(PendingUnswizzle& task, Image& image sparse_hint, false); task.last_submitted_offset += static_cast(z_count) * task.bytes_per_slice; + task.current_batch_start_byte = task.current_offset; } } @@ -1720,13 +1741,6 @@ void TextureCache

::TickAsyncUnswizzleGpu(PendingUnswizzle& task, Image& image } } -// Send help, I didn't think this would be that complicated to implement -// Whatever, this needlessly over complicated block of code is this way simply to save RAM -// -// Note for Liz: -// Divided everything into helper functions but don't know if this makes everything more confusing. -// Or could possibly cause a speed reduction, I'm not entirely sure what template functions look like in ASM -// So I am unsure if there will be overhead with saving the return location into the stack template void TextureCache

::InitializeCpuUnswizzleTask(PendingUnswizzle& task, Image& image) { task.total_size = MapSizeBytes(image); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 24eb618af0..d7ef9c3feb 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -159,6 +159,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches> sparse_segments;