diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index d09578425b..46418aecc3 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -709,9 +709,12 @@ void BlockLinearUnswizzle3DPass::Unswizzle( using namespace VideoCommon::Accelerated; const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth); + static constexpr u32 MAX_WINDOW_SLICES = 4; + static constexpr float AREA_GROWTH_LIMIT = 3.0f; // Removing the if (!image.has_compute_unswizzle_buffer) check here is not ideal but MAX_BATCH_SLICES can changed mid-way through and I don't want to cause device loss or corruption - image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES); + // This may cause issues so needs thorough testing, if works fine this is a win to reduce vram usage + image.AllocateComputeUnswizzleBuffer((std::min)(MAX_BATCH_SLICES, MAX_WINDOW_SLICES)); ASSERT(swizzles.size() == 1); const auto& sw = swizzles[0]; @@ -787,25 +790,8 @@ void BlockLinearUnswizzle3DPass::Unswizzle( 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 junk data appears - //UnswizzleZeroChunk(image, z_dst, sub_len); + /*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; @@ -823,11 +809,60 @@ void BlockLinearUnswizzle3DPass::Unswizzle( }; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, barrier); - }); + });*/ + + u32 win_offset = 0; + while (win_offset < sub_len) { + const u32 wz_start = z_src + win_offset; + const u32 wz_dst = z_dst + win_offset; + + u32 win_len = 0; + u32 ux0 = blocks_x, uy0 = blocks_y, ux1 = 0, uy1 = 0; + u64 slice_area_sum = 0; + bool any_in_window = false; + + while (win_offset + win_len < sub_len && win_len < MAX_WINDOW_SLICES) { + const u32 z = wz_start + win_len; + const bool have_box = !slice_bounds.empty() && + z < static_cast(slice_bounds.size()); + const auto b = have_box ? slice_bounds[z] : SliceBBox{}; + const bool slice_populated = b.x1 > b.x0 && b.y1 > b.y0; + + if (slice_populated) { + const u32 cx0 = any_in_window ? (std::min)(ux0, b.x0) : b.x0; + const u32 cy0 = any_in_window ? (std::min)(uy0, b.y0) : b.y0; + const u32 cx1 = any_in_window ? (std::max)(ux1, b.x1) : b.x1; + const u32 cy1 = any_in_window ? (std::max)(uy1, b.y1) : b.y1; + const u64 candidate_area = + static_cast(cx1 - cx0) * (cy1 - cy0); + const u64 candidate_slice_sum = slice_area_sum + + static_cast(b.x1 - b.x0) * (b.y1 - b.y0); + + if (any_in_window && static_cast(candidate_area) > (AREA_GROWTH_LIMIT * static_cast(candidate_slice_sum))) { + break; + } + + ux0 = cx0; uy0 = cy0; ux1 = cx1; uy1 = cy1; + slice_area_sum = candidate_slice_sum; + any_in_window = true; + } + ++win_len; + } + if (win_len == 0) { + win_len = 1; + } - UnswizzleChunk(image, swizzled, sw, params, - ox0, oy0, ox1 - ox0, oy1 - oy0, - z_src, z_dst, sub_len); + const u32 ox0 = any_in_window ? ux0 : 0; + const u32 oy0 = any_in_window ? uy0 : 0; + const u32 ox1 = any_in_window ? ux1 : blocks_x; + const u32 oy1 = any_in_window ? uy1 : blocks_y; + + UnswizzleChunk(image, swizzled, sw, params, + ox0, oy0, ox1 - ox0, oy1 - oy0, + wz_start, wz_dst, win_len); + + win_offset += win_len; + } sub_offset += sub_len; } diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp index b4f4ea3b08..4c3f724d79 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.cpp +++ b/src/video_core/texture_cache/accelerated_swizzle.cpp @@ -20,9 +20,6 @@ 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; @@ -69,56 +66,4 @@ 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 32b18f3c89..b4f725fc23 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.h +++ b/src/video_core/texture_cache/accelerated_swizzle.h @@ -11,6 +11,9 @@ namespace VideoCommon::Accelerated { +constexpr u32 GOB_SIZE_Y = 8; +constexpr u32 GOB_SIZE = 512; + struct BlockLinearSwizzle2DParams { alignas(16) std::array origin; alignas(16) std::array destination; @@ -45,10 +48,75 @@ struct SliceBBox { [[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); +template +void ForEachZInGroupOverlap(u64 local_start, u64 local_end, + u32 block_size, u32 x_shift, u32 block_height, + u32 block_height_mask, u32 block_depth, u32 block_depth_mask, + u32 bytes_per_block, u32 blocks_x, u32 blocks_y, + Callback&& on_result) { + if (local_start >= local_end) return; + + const u32 z_pitch = GOB_SIZE << block_height; + 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 u32 zz_count = 1u << block_depth; + + const u64 band_start = local_start / block_size; + const u64 band_end = (local_end - 1) / block_size; + + if (band_start != band_end) { + SliceBBox box; + box.x0 = 0; + box.x1 = blocks_x; + box.y0 = static_cast(band_start) * y_per_band; + box.y1 = (std::min)(static_cast(band_end + 1) * y_per_band, blocks_y); + for (u32 zz = 0; zz < zz_count; ++zz) on_result(zz, box); + return; + } + + const u64 wb_start = local_start - band_start * block_size; + const u64 wb_end = (local_end - 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) { + SliceBBox box; + box.x0 = col_start * blocks_per_column; + box.x1 = (std::min)((col_end + 1) * blocks_per_column, blocks_x); + box.y0 = static_cast(band_start) * y_per_band; + box.y1 = (std::min)(box.y0 + y_per_band, blocks_y); + for (u32 zz = 0; zz < zz_count; ++zz) on_result(zz, box); + return; + } + + const u64 wc_start = wb_start - static_cast(col_start) * column_pitch; + const u64 wc_end = wb_end - static_cast(col_start) * column_pitch; + const u32 zz_start = static_cast(wc_start / z_pitch); + const u32 zz_end = static_cast(wc_end / z_pitch); + + if (zz_start != zz_end) { + SliceBBox box; + box.x0 = col_start * blocks_per_column; + box.x1 = (std::min)((col_start + 1) * blocks_per_column, blocks_x); + box.y0 = static_cast(band_start) * y_per_band; + box.y1 = (std::min)(box.y0 + y_per_band, blocks_y); + for (u32 zz = zz_start; zz <= zz_end; ++zz) on_result(zz, box); + return; + } + + const u64 wz_start = wc_start - static_cast(zz_start) * z_pitch; + const u64 wz_end = wc_end - static_cast(zz_start) * z_pitch; + const u32 row_start = static_cast(wz_start) / GOB_SIZE; + const u32 row_end = static_cast(wz_end) / GOB_SIZE; + + SliceBBox box; + box.x0 = col_start * blocks_per_column; + box.x1 = (std::min)((col_start + 1) * blocks_per_column, blocks_x); + box.y0 = (static_cast(band_start) * rows_per_group + row_start) * GOB_SIZE_Y; + box.y1 = (std::min)((static_cast(band_start) * rows_per_group + row_end + 1) * GOB_SIZE_Y, blocks_y); + on_result(zz_start, box); +} } // namespace VideoCommon::Accelerated diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index d7dd9eb17e..1ee8c62e84 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -184,7 +184,6 @@ void TextureCache

::TickFrame() { sentenced_image_view.Tick(); TickAsyncDecode(); TickAsyncUnswizzle(); - //TickCompletedSparseImages(); runtime.TickFrame(); ++frame_tick; @@ -1427,12 +1426,16 @@ void TextureCache

::TickAsyncUnswizzle() { const auto segs = gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); task.sparse_segments.assign(segs.begin(), segs.end()); + + std::sort(task.sparse_segments.begin(), task.sparse_segments.end(), + [](const auto& a, const auto& b) { return a.first < b.first; }); + 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()) { + if (image.info.size.depth > 1) { const auto uploads = FullUploadSwizzles(task.info); const auto sp = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams( uploads[0], task.info); @@ -1440,34 +1443,40 @@ 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; + const u32 zz_count = 1u << sp.block_depth; + const u32 total_groups = Common::DivCeil( + static_cast(image.info.size.depth), zz_count); + u32 group_watermark = 0; + for (const auto& [seg_gpu_addr, seg_size] : task.sparse_segments) { const u64 seg_start = seg_gpu_addr - image.gpu_addr; const u64 seg_end = seg_start + seg_size; - while (z_watermark < static_cast(image.info.size.depth) && - image.slice_offsets[z_watermark] + swizzled_slice_size <= seg_start) { - ++z_watermark; + while (group_watermark < total_groups && + static_cast(group_watermark) * swizzled_slice_size + swizzled_slice_size <= seg_start) { + ++group_watermark; } - 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) { + + for (u32 g = group_watermark; g < total_groups; ++g) { + const u64 group_base = static_cast(g) * swizzled_slice_size; + if (group_base >= seg_end) break; + + const u64 local_start = (std::max)(seg_start, group_base) - group_base; + const u64 local_end = (std::min)(seg_end, group_base + swizzled_slice_size) - group_base; + + VideoCommon::Accelerated::ForEachZInGroupOverlap( + local_start, local_end, + sp.block_size, sp.x_shift, sp.block_height, sp.block_height_mask, + sp.block_depth, sp.block_depth_mask, bytes_per_block, blocks_x, blocks_y, + [&](u32 zz, const VideoCommon::Accelerated::SliceBBox& box) { + const u32 z = g * zz_count + zz; + if (z >= static_cast(image.info.size.depth)) return; + + task.slice_has_data[z] = 1u; + auto& acc = task.slice_bounds[z]; if (acc.x1 <= acc.x0 || acc.y1 <= acc.y0) { acc = box; } else { @@ -1476,8 +1485,7 @@ void TextureCache

::TickAsyncUnswizzle() { acc.x1 = (std::max)(acc.x1, box.x1); acc.y1 = (std::max)(acc.y1, box.y1); } - } - } + }); } } } else { @@ -1616,22 +1624,6 @@ void TextureCache

::TickAsyncUnswizzle() { (is_final_batch && bytes_ready < task.bytes_per_slice); if (is_final_batch && all_submitted) { - /*if (task.is_sparse && !task.is_incremental) { - const auto segs = - gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); - CompletedSparseImage entry; - entry.image_id = task.image_id; - entry.info = task.info; - entry.gpu_addr = image.gpu_addr; - entry.guest_size_bytes = image.guest_size_bytes; - entry.last_segments.assign(segs.begin(), segs.end()); - entry.slice_uploaded = task.slice_has_data; - entry.bytes_per_slice = task.bytes_per_slice; - entry.swizzled_slice_size = task.swizzled_slice_size; - entry.swizzle_block_depth = task.swizzle_block_depth; - completed_sparse_images.push_back(std::move(entry)); - }*/ - runtime.FreeDeferredStagingBuffer(task.staging_buffer); image.flags &= ~ImageFlagBits::IsDecoding; runtime.ReleaseSparseUnswizzleBuffer(image); @@ -1639,126 +1631,6 @@ void TextureCache

::TickAsyncUnswizzle() { } } -// This is my poor attempt at trying to detect if a sparse texture had been remapped and just reprocess what was changed -// This may or may not be slower in some circumstances or not work at all as about halfway through I fried my brain -template -void TextureCache

::TickCompletedSparseImages() { - if (completed_sparse_images.empty()) return; - - for (auto it = completed_sparse_images.begin(); it != completed_sparse_images.end(); ) { - CompletedSparseImage& entry = *it; - - Image& image = slot_images[entry.image_id]; - - if (True(image.flags & ImageFlagBits::IsDecoding)) { - ++it; - continue; - } - - const auto raw_segs = - gpu_memory->GetSubmappedRange(entry.gpu_addr, entry.guest_size_bytes); - const std::vector> current_segs(raw_segs.begin(), raw_segs.end()); - - if (current_segs == entry.last_segments) { - ++it; - continue; - } - - std::vector> new_segs; - { - size_t li = 0; - for (const auto& cseg : current_segs) { - while (li < entry.last_segments.size() && - entry.last_segments[li].first < cseg.first) { - ++li; - } - const bool already_known = - li < entry.last_segments.size() && - entry.last_segments[li].first == cseg.first && - entry.last_segments[li].second == cseg.second; - if (!already_known) { - new_segs.push_back(cseg); - } - } - } - - entry.last_segments = current_segs; - - if (new_segs.empty()) { - ++it; - continue; - } - - if (entry.swizzle_block_depth != 0 || entry.swizzled_slice_size == 0) { - QueueAsyncUnswizzle(image, entry.image_id); - ++it; - continue; - } - - std::vector new_slice_bm(image.info.size.depth, 0u); - u32 z_min = image.info.size.depth; - u32 z_max = 0; - - for (const auto& [seg_gpu, seg_size] : new_segs) { - const u64 seg_abs_start = seg_gpu - entry.gpu_addr; - const u64 seg_abs_end = seg_abs_start + seg_size; - - u32 z_first = static_cast(seg_abs_start / entry.swizzled_slice_size); - u32 z_last = static_cast((seg_abs_end - 1) / entry.swizzled_slice_size); - z_first = (std::min)(z_first, image.info.size.depth - 1); - z_last = (std::min)(z_last, image.info.size.depth - 1); - - for (u32 z = z_first; z <= z_last; ++z) { - if (entry.slice_uploaded[z]) continue; - new_slice_bm[z] = 1u; - z_min = (std::min)(z_min, z); - z_max = (std::max)(z_max, z); - } - } - - if (z_min > z_max) { - ++it; - continue; - } - - const u32 z_count = z_max - z_min + 1; - - image.flags |= ImageFlagBits::IsDecoding; - - PendingUnswizzle task{}; - task.image_id = entry.image_id; - task.info = entry.info; - task.is_sparse = true; - task.is_incremental = true; - task.staging_base_byte_offset = - static_cast(z_min) * entry.swizzled_slice_size; - task.total_size = - static_cast(z_count) * entry.swizzled_slice_size; - task.incremental_z_start = z_min; - task.incremental_z_count = z_count; - task.bytes_per_slice = entry.bytes_per_slice; - task.swizzled_slice_size = entry.swizzled_slice_size; - task.swizzle_block_depth = entry.swizzle_block_depth; - task.last_submitted_offset = 0; - - task.slice_has_data.resize(z_count, 0u); - for (u32 z = z_min; z <= z_max; ++z) { - task.slice_has_data[z - z_min] = new_slice_bm[z]; - } - - task.sparse_segments = std::move(new_segs); - task.segment_scan_cursor = 0; - - unswizzle_queue.push_front(std::move(task)); - - for (u32 z = z_min; z <= z_max; ++z) { - entry.slice_uploaded[z] |= new_slice_bm[z]; - } - - ++it; - } -} - template bool TextureCache

::ScaleUp(Image& image) { const bool has_copy = image.HasScaled(); @@ -2715,11 +2587,6 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { } slot_images.erase(image_id); - std::erase_if(completed_sparse_images, - [image_id](const CompletedSparseImage& e) { - return e.image_id == image_id; - }); - alloc_images.erase(alloc_image_it); if (alloc_images.empty()) { image_allocs_table.erase(alloc_it); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 162f483192..755085ec43 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -438,7 +438,6 @@ private: void QueueAsyncUnswizzle(Image& image, ImageId image_id); void TickAsyncUnswizzle(); - void TickCompletedSparseImages(); struct CompletedSparseImage { ImageId image_id; @@ -541,7 +540,6 @@ private: std::vector> async_decodes; std::deque unswizzle_queue; - std::deque completed_sparse_images; // Join caching boost::container::small_vector join_overlap_ids;