diff options
Diffstat (limited to 'src/video_core/engines/sw_blitter/blitter.cpp')
| -rw-r--r-- | src/video_core/engines/sw_blitter/blitter.cpp | 104 |
1 files changed, 87 insertions, 17 deletions
diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp index 8bcc2f7a76..78363bed11 100644 --- a/src/video_core/engines/sw_blitter/blitter.cpp +++ b/src/video_core/engines/sw_blitter/blitter.cpp @@ -5,6 +5,8 @@ #include <cmath> #include <vector> +#include "common/alignment.h" +#include "common/literals.h" #include "common/scratch_buffer.h" #include "video_core/engines/sw_blitter/blitter.h" #include "video_core/engines/sw_blitter/converter.h" @@ -23,6 +25,7 @@ using VideoCore::Surface::PixelFormatFromRenderTargetFormat; namespace Tegra::Engines::Blitter { using namespace Texture; +using namespace Common::Literals; namespace { @@ -125,6 +128,46 @@ void ProcessPitchLinear(std::span<const u8> input, std::span<u8> output, size_t } } +struct SubSurface { + u64 offset; ///< From the surface's address + size_t size; ///< Bytes + u32 height; ///< Lines the part has + u32 origin_y; ///< The blit's first line in it +}; + +// The part of a surface a blit covers: the rows of blocks (block linear) or the lines (pitch) from +// its first line to its last. A tile copied into a layer of a large texture used to read and write +// back the whole layer (64-256 MiB), which also gave every block of a Switch 2 sparse layer memory. +SubSurface CoveredRows(const Fermi2D::Surface& surface, u32 bytes_per_pixel, s32 x0, s32 y0, + u32 extent_x, u32 extent_y, size_t surface_size) { + const SubSurface whole{0, surface_size, surface.height, static_cast<u32>(y0)}; + if (x0 < 0 || y0 < 0 || extent_y == 0) { + return whole; + } + const u64 first_line = static_cast<u32>(y0); + if (surface.linear == Fermi2D::MemoryLayout::BlockLinear) { + if (surface.depth != 1) { + return whole; + } + const u32 block_height = surface.block_height; + const u32 block_depth = surface.block_depth; + const u32 lines_shift = GOB_SIZE_Y_SHIFT + block_height; + const u64 row_size = + u64{Common::AlignUpLog2(surface.width * bytes_per_pixel, GOB_SIZE_X_SHIFT)} + << (lines_shift + block_depth); + const u64 first_row = first_line >> lines_shift; + const u64 rows = ((first_line + extent_y - 1) >> lines_shift) - first_row + 1; + const SubSurface part{first_row * row_size, static_cast<size_t>(rows * row_size), + static_cast<u32>(rows << lines_shift), + static_cast<u32>(first_line - (first_row << lines_shift))}; + return part.offset + part.size <= surface_size ? part : whole; + } + const u64 size = (extent_y - 1) * u64{surface.pitch} + + (static_cast<u64>(x0) + extent_x) * bytes_per_pixel; + const SubSurface part{first_line * surface.pitch, static_cast<size_t>(size), extent_y, 0}; + return part.offset + part.size <= surface_size ? part : whole; +} + } // namespace struct SoftwareBlitEngine::BlitEngineImpl { @@ -133,6 +176,23 @@ struct SoftwareBlitEngine::BlitEngineImpl { Common::ScratchBuffer<u8> dst_buffer; Common::ScratchBuffer<f32> intermediate_src; Common::ScratchBuffer<f32> intermediate_dst; + + // Buffers grown for one large surface are freed after the blit rather than kept for the rest + // of the session; small ones stay. 16 MiB kept title 1's boot mip blits (64/16/4/1 MiB levels) + // in 16 such buffers, 256 MiB, across its 2D engines. + void ReleaseLargeBuffers() { + constexpr size_t LargeCapacity = 2_MiB; + for (auto* const buffer : {&tmp_buffer, &src_buffer, &dst_buffer}) { + if (buffer->capacity() > LargeCapacity) { + buffer->release(); + } + } + for (auto* const buffer : {&intermediate_src, &intermediate_dst}) { + if (buffer->capacity() * sizeof(f32) > LargeCapacity) { + buffer->release(); + } + } + } ConverterFactory converter_factory; }; @@ -161,9 +221,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format)); const size_t src_size = get_surface_size(src, src_bytes_per_pixel); + const SubSurface src_part = CoveredRows(src, src_bytes_per_pixel, config.src_x0, config.src_y0, + src_extent_x, src_extent_y, src_size); Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::SafeRead> tmp_buffer( - memory_manager, src.Address(), src_size, &impl->tmp_buffer); + memory_manager, src.Address() + src_part.offset, src_part.size, &impl->tmp_buffer); const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel; const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel; @@ -202,12 +264,13 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, impl->dst_buffer.resize_destructive(dst_copy_size); if (src.linear == Fermi2D::MemoryLayout::BlockLinear) { - UnswizzleSubrect(impl->src_buffer, tmp_buffer, src_bytes_per_pixel, src.width, src.height, - src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, - src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); + UnswizzleSubrect(impl->src_buffer, tmp_buffer, src_bytes_per_pixel, src.width, + src_part.height, src.depth, config.src_x0, src_part.origin_y, + src_extent_x, src_extent_y, src.block_height, src.block_depth, + src_extent_x * src_bytes_per_pixel); } else { ProcessPitchLinear<false>(tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, - src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); + src.pitch, config.src_x0, src_part.origin_y, src_bytes_per_pixel); } // Conversion Phase @@ -222,18 +285,25 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, } const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel); - Tegra::Memory::GpuGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::SafeReadWrite> - tmp_buffer2(memory_manager, dst.Address(), dst_size, &impl->tmp_buffer); - - if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) { - SwizzleSubrect(tmp_buffer2, impl->dst_buffer, dst_bytes_per_pixel, dst.width, dst.height, - dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, - dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); - } else { - ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, - dst.pitch, config.dst_x0, config.dst_y0, - static_cast<size_t>(dst_bytes_per_pixel)); - } + const SubSurface dst_part = CoveredRows(dst, dst_bytes_per_pixel, config.dst_x0, config.dst_y0, + dst_extent_x, dst_extent_y, dst_size); + { + Tegra::Memory::GpuGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::SafeReadWrite> + tmp_buffer2(memory_manager, dst.Address() + dst_part.offset, dst_part.size, + &impl->tmp_buffer); + + if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) { + SwizzleSubrect(tmp_buffer2, impl->dst_buffer, dst_bytes_per_pixel, dst.width, + dst_part.height, dst.depth, config.dst_x0, dst_part.origin_y, + dst_extent_x, dst_extent_y, dst.block_height, dst.block_depth, + dst_extent_x * dst_bytes_per_pixel); + } else { + ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, + dst.pitch, config.dst_x0, dst_part.origin_y, + static_cast<size_t>(dst_bytes_per_pixel)); + } + } // tmp_buffer2 writes the destination back here + impl->ReleaseLargeBuffers(); return true; } |
