@ -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 {