Browse Source

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
pull/3737/head
Forrest Mark X 1 month ago
committed by crueter
parent
commit
b40b9ecdef
  1. 39
      src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp
  2. 4
      src/video_core/renderer_opengl/gl_texture_cache.cpp
  3. 11
      src/video_core/renderer_opengl/gl_texture_cache.h
  4. 193
      src/video_core/renderer_vulkan/vk_compute_pass.cpp
  5. 6
      src/video_core/renderer_vulkan/vk_compute_pass.h
  6. 5
      src/video_core/renderer_vulkan/vk_texture_cache.cpp
  7. 10
      src/video_core/renderer_vulkan/vk_texture_cache.h
  8. 55
      src/video_core/texture_cache/accelerated_swizzle.cpp
  9. 10
      src/video_core/texture_cache/accelerated_swizzle.h
  10. 28
      src/video_core/texture_cache/texture_cache.h
  11. 3
      src/video_core/texture_cache/texture_cache_base.h

39
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;
}
}

4
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<const SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
[[maybe_unused]] std::span<const u8> slice_has_data,
[[maybe_unused]] bool image_already_uploaded) {
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds, [[maybe_unused]] bool image_already_uploaded) {
switch (image.info.type) {
case ImageType::e2D:
if (IsPixelFormatASTC(image.info.format)) {

11
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<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data = {},
bool image_already_uploaded = false);
void AccelerateImageUpload(Image&, const StagingBufferMap&,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data = {},
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds = {},
bool image_already_uploaded = false);
void InsertUploadMemoryBarrier();

193
src/video_core/renderer_vulkan/vk_compute_pass.cpp

@ -703,6 +703,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> 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<u32>(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<Run, 16> 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<u32>(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<u32>(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<Run, 16> 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<u32>(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<VkDeviceSize>(blocks_x) * blocks_y * bytes_per_block;
static_cast<VkDeviceSize>(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<s32>(origin_x * 4u);
const s32 dst_y = static_cast<s32>(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<s32>(z_dst)}, // Write to correct Z
.imageExtent = {image_width, image_height, z_count},
.imageOffset = {dst_x, dst_y, static_cast<s32>(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<VkDeviceSize>(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{

6
src/video_core/renderer_vulkan/vk_compute_pass.h

@ -150,6 +150,7 @@ public:
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds,
bool image_already_uploaded);
void UnswizzleChunk(
@ -157,13 +158,12 @@ public:
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);
void UnswizzleZeroChunk(
Image &image,
u32 blocks_x, u32 blocks_y,
u32 bytes_per_block,
u32 z_dst, u32 z_count);
private:

5
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<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> 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);

10
src/video_core/renderer_vulkan/vk_texture_cache.h

@ -92,10 +92,11 @@ public:
}
void AccelerateImageUpload(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data = {},
bool image_already_uploaded = false);
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data = {},
std::span<const VideoCommon::Accelerated::SliceBBox> 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);

55
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<u32>(wb_start / column_pitch);
const u32 col_end = static_cast<u32>(wb_end / column_pitch);
if (col_start == col_end) {
const u32 row_start = static_cast<u32>(wb_start % column_pitch) / GOB_SIZE;
const u32 row_end = static_cast<u32>(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<u32>(band_start) * rows_per_group + row_start) * GOB_SIZE_Y;
box.y1 = (static_cast<u32>(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<u32>(band_start) * y_per_band;
box.y1 = box.y0 + y_per_band;
}
} else {
box.x0 = 0;
box.x1 = blocks_x;
box.y0 = static_cast<u32>(band_start) * y_per_band;
box.y1 = static_cast<u32>(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

10
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

28
src/video_core/texture_cache/texture_cache.h

@ -1430,6 +1430,7 @@ void TextureCache<P>::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<P>::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<P>::TickAsyncUnswizzle() {
for (u32 z = z_watermark; z < static_cast<u32>(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<P>::TickAsyncUnswizzle() {
FixSmallVectorADL(uploads),
z_src, z_image, z_count,
sparse_hint,
std::span<const VideoCommon::Accelerated::SliceBBox>(task.slice_bounds),
task.is_incremental);
task.last_submitted_offset += static_cast<size_t>(z_count) * task.bytes_per_slice;
}

3
src/video_core/texture_cache/texture_cache_base.h

@ -25,6 +25,8 @@
#include "common/literals.h"
#include "common/lru_cache.h"
#include <ranges>
#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<TextureCacheChannelI
bool initialized = false;
bool is_sparse = false;
std::vector<u8> slice_has_data;
std::vector<VideoCommon::Accelerated::SliceBBox> slice_bounds;
std::vector<std::pair<GPUVAddr, size_t>> sparse_segments;
size_t segment_scan_cursor = 0;
u64 swizzled_slice_size = 0;

Loading…
Cancel
Save