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
Forrest Mark X 3 months 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 #version 430
#ifdef VULKAN #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 BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0 #define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1 #define BINDING_OUTPUT_BUFFER 1
#else #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 BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout(location = n) uniform #define UNIFORM(n) layout(location = n) uniform
@ -64,17 +55,9 @@ END_PUSH_CONSTANTS
#endif #endif
// --- Buffers --- // --- 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 InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; 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 { layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU64 {
uvec2 out_u64[]; uvec2 out_u64[];
}; };
@ -113,20 +96,10 @@ uint SwizzleOffset(uvec2 pos) {
} }
uvec4 ReadTexel(uint offset) { 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() { void main() {
@ -162,11 +135,7 @@ void main() {
if (pc.bytes_per_block_log2 == 4u) { // 16 bytes (BC3/BC7) if (pc.bytes_per_block_log2 == 4u) { // 16 bytes (BC3/BC7)
out_u128[block_index] = texel; 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; 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); 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, std::span<const SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count, u32 z_src_start, u32 z_image_start, u32 z_count,
[[maybe_unused]] std::span<const u8> slice_has_data, [[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) { switch (image.info.type) {
case ImageType::e2D: case ImageType::e2D:
if (IsPixelFormatASTC(image.info.format)) { 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, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation); 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(); 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, std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count, u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data, std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds,
bool image_already_uploaded) bool image_already_uploaded)
{ {
using namespace VideoCommon::Accelerated; using namespace VideoCommon::Accelerated;
@ -718,7 +719,6 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
const u32 blocks_x = (image.info.size.width + 3) / 4; const u32 blocks_x = (image.info.size.width + 3) / 4;
const u32 blocks_y = (image.info.size.height + 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 const VkImageLayout initial_prior_layout = image_already_uploaded
? VK_IMAGE_LAYOUT_GENERAL ? VK_IMAGE_LAYOUT_GENERAL
@ -752,28 +752,99 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, pre_barrier); 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 { } 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 StagingBufferRef& swizzled,
const VideoCommon::SwizzleParameters& sw, const VideoCommon::SwizzleParameters& sw,
const BlockLinearSwizzle3DParams& params, 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) u32 z_src, u32 z_dst, u32 z_count)
{ {
image.compute_unswizzle_buffer_is_zero = false;
BlockLinearUnswizzle3DPushConstants pc{}; 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.origin[2] = z_src; // Current chunk's Z start
pc.destination[0] = params.destination[0]; pc.destination[0] = params.destination[0];
@ -826,8 +900,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
pc.block_depth = params.block_depth; pc.block_depth = params.block_depth;
pc.block_depth_mask = params.block_depth_mask; 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 pc.blocks_dim[2] = z_count; // Only process the count
compute_pass_descriptor_queue.Acquire(scheduler, 3); compute_pass_descriptor_queue.Acquire(scheduler, 3);
@ -840,25 +914,27 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
const VkDescriptorSet set = descriptor_allocator.Commit(); 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 gz = Common::DivCeil(z_count, 4u);
const u32 bytes_per_block = 1u << pc.bytes_per_block_log2; const u32 bytes_per_block = 1u << pc.bytes_per_block_log2;
const VkDeviceSize output_slice_size = 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 VkDeviceSize barrier_size = output_slice_size * z_count;
const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkBuffer out_buffer = *image.compute_unswizzle_buffer;
const VkImage dst_image = image.Handle(); const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask(); 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, scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz,
z_dst, z_count, barrier_size, z_dst, z_count, barrier_size,
out_buffer, dst_image, aspect, 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) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return; return;
@ -882,7 +958,6 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
.offset = 0, .offset = 0,
.size = barrier_size, .size = barrier_size,
}; };
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 chunk to correct Z position in image // Copy chunk to correct Z position in image
@ -891,8 +966,8 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
.bufferRowLength = 0, .bufferRowLength = 0,
.bufferImageHeight = 0, .bufferImageHeight = 0,
.imageSubresource = {aspect, 0, 0, 1}, .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, cmdbuf.CopyBufferToImage(out_buffer, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
@ -999,8 +1074,6 @@ void MSAACopyPass::CopyImage(Image& dst_image, Image& src_image,
// So enjoy this mess // So enjoy this mess
void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk( void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
Image& image, Image& image,
u32 blocks_x, u32 blocks_y,
u32 bytes_per_block,
u32 z_dst, u32 z_count) u32 z_dst, u32 z_count)
{ {
ASSERT(image.has_compute_unswizzle_buffer); ASSERT(image.has_compute_unswizzle_buffer);
@ -1010,40 +1083,42 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
const VkImageAspectFlags aspect = image.AspectMask(); const VkImageAspectFlags aspect = image.AspectMask();
const u32 image_width = image.info.size.width; const u32 image_width = image.info.size.width;
const u32 image_height = image.info.size.height; 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, 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) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return; 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. // Copy the zeroed buffer region into the correct Z position of the image.
const VkBufferImageCopy copy{ const VkBufferImageCopy copy{

6
src/video_core/renderer_vulkan/vk_compute_pass.h

@ -150,6 +150,7 @@ public:
std::span<const VideoCommon::SwizzleParameters> swizzles, std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count, u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data, std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds,
bool image_already_uploaded); bool image_already_uploaded);
void UnswizzleChunk( void UnswizzleChunk(
@ -157,13 +158,12 @@ public:
const StagingBufferRef &swizzled, const StagingBufferRef &swizzled,
const VideoCommon::SwizzleParameters &sw, const VideoCommon::SwizzleParameters &sw,
const BlockLinearSwizzle3DParams &params, 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); u32 z_src, u32 z_dst, u32 z_count);
void UnswizzleZeroChunk( void UnswizzleZeroChunk(
Image &image, Image &image,
u32 blocks_x, u32 blocks_y,
u32 bytes_per_block,
u32 z_dst, u32 z_count); u32 z_dst, u32 z_count);
private: 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); runtime->memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal);
has_compute_unswizzle_buffer = true; has_compute_unswizzle_buffer = true;
compute_unswizzle_buffer_is_zero = false;
} }
void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
@ -2839,6 +2840,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
std::span<const VideoCommon::SwizzleParameters> swizzles, std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count, u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data, std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds,
bool image_already_uploaded) { bool image_already_uploaded) {
if (IsPixelFormatASTC(image.info.format)) { if (IsPixelFormatASTC(image.info.format)) {
@ -2861,7 +2863,8 @@ void TextureCacheRuntime::AccelerateImageUpload(
return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles,
z_src_start, z_image_start, z_count, z_src_start, z_image_start, z_count,
slice_has_data, image_already_uploaded);
slice_has_data, slice_bounds,
image_already_uploaded);
} }
ASSERT(false); ASSERT(false);

10
src/video_core/renderer_vulkan/vk_texture_cache.h

@ -92,10 +92,11 @@ public:
} }
void AccelerateImageUpload(Image&, const StagingBufferRef&, 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() {} void InsertUploadMemoryBarrier() {}
@ -341,6 +342,7 @@ private:
vk::Buffer compute_unswizzle_buffer; vk::Buffer compute_unswizzle_buffer;
VkDeviceSize compute_unswizzle_buffer_size = 0; VkDeviceSize compute_unswizzle_buffer_size = 0;
bool has_compute_unswizzle_buffer = false; bool has_compute_unswizzle_buffer = false;
bool compute_unswizzle_buffer_is_zero = false;
void AllocateComputeUnswizzleBuffer(u32 max_slices); 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 Tegra::Texture::GOB_SIZE_Y_SHIFT;
using VideoCore::Surface::BytesPerBlock; using VideoCore::Surface::BytesPerBlock;
constexpr u32 GOB_SIZE_Y = 8;
constexpr u32 GOB_SIZE = 512;
BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameters& swizzle, BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameters& swizzle,
const ImageInfo& info) { const ImageInfo& info) {
const Extent3D block = swizzle.block; 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 } // namespace VideoCommon::Accelerated

10
src/video_core/texture_cache/accelerated_swizzle.h

@ -35,10 +35,20 @@ struct BlockLinearSwizzle3DParams {
u32 block_depth_mask; u32 block_depth_mask;
}; };
struct SliceBBox {
u32 x0 = 0, y0 = 0, x1 = 0, y1 = 0;
};
[[nodiscard]] BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams( [[nodiscard]] BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(
const SwizzleParameters& swizzle, const ImageInfo& info); const SwizzleParameters& swizzle, const ImageInfo& info);
[[nodiscard]] BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams( [[nodiscard]] BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(
const SwizzleParameters& swizzle, const ImageInfo& info); 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 } // 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.segment_scan_cursor = 0;
task.slice_has_data.assign(image.info.size.depth, 0u); 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 && !image.slice_offsets.empty()) {
const auto uploads = FullUploadSwizzles(task.info); const auto uploads = FullUploadSwizzles(task.info);
@ -1439,6 +1440,10 @@ void TextureCache<P>::TickAsyncUnswizzle() {
task.swizzled_slice_size = swizzled_slice_size; task.swizzled_slice_size = swizzled_slice_size;
task.swizzle_block_depth = sp.block_depth; 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; u32 z_watermark = 0;
for (const auto& [seg_gpu_addr, seg_size] : task.sparse_segments) { for (const auto& [seg_gpu_addr, seg_size] : task.sparse_segments) {
const u64 seg_start = seg_gpu_addr - image.gpu_addr; 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) { for (u32 z = z_watermark; z < static_cast<u32>(image.info.size.depth); ++z) {
if (image.slice_offsets[z] >= seg_end) break; if (image.slice_offsets[z] >= seg_end) break;
task.slice_has_data[z] = 1u; 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 { } else {
@ -1577,6 +1604,7 @@ void TextureCache<P>::TickAsyncUnswizzle() {
FixSmallVectorADL(uploads), FixSmallVectorADL(uploads),
z_src, z_image, z_count, z_src, z_image, z_count,
sparse_hint, sparse_hint,
std::span<const VideoCommon::Accelerated::SliceBBox>(task.slice_bounds),
task.is_incremental); task.is_incremental);
task.last_submitted_offset += static_cast<size_t>(z_count) * task.bytes_per_slice; 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/literals.h"
#include "common/lru_cache.h" #include "common/lru_cache.h"
#include <ranges> #include <ranges>
#include "accelerated_swizzle.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/slot_vector.h" #include "common/slot_vector.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
@ -141,6 +143,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
bool initialized = false; bool initialized = false;
bool is_sparse = false; bool is_sparse = false;
std::vector<u8> slice_has_data; std::vector<u8> slice_has_data;
std::vector<VideoCommon::Accelerated::SliceBBox> slice_bounds;
std::vector<std::pair<GPUVAddr, size_t>> sparse_segments; std::vector<std::pair<GPUVAddr, size_t>> sparse_segments;
size_t segment_scan_cursor = 0; size_t segment_scan_cursor = 0;
u64 swizzled_slice_size = 0; u64 swizzled_slice_size = 0;

Loading…
Cancel
Save