2020-12-18 15:43:34 +01:00
# include "stdafx.h"
2025-02-11 03:00:37 +01:00
# include "Emu/RSX/VK/VKGSRenderTypes.hpp"
2020-12-18 15:43:34 +01:00
# include "VKTextureCache.h"
2020-12-18 18:35:02 +01:00
# include "VKCompute.h"
2025-02-11 03:00:37 +01:00
# include "VKAsyncScheduler.h"
2020-12-18 15:43:34 +01:00
2025-10-05 18:28:03 +02:00
# include "rx/asm.hpp"
2020-12-18 15:43:34 +01:00
namespace vk
{
2022-07-24 19:28:57 +02:00
u64 hash_image_properties ( VkFormat format , u16 w , u16 h , u16 d , u16 mipmaps , VkImageType type , VkImageCreateFlags create_flags , VkSharingMode sharing_mode )
2022-02-06 12:23:46 +01:00
{
2022-03-01 18:41:02 +01:00
/**
2025-04-05 21:50:45 +02:00
* Key layout :
* 00 - 08 : Format ( Max 255 )
* 08 - 24 : Width ( Max 64 K )
* 24 - 40 : Height ( Max 64 K )
* 40 - 48 : Depth ( Max 255 )
* 48 - 54 : Mipmaps ( Max 63 ) < - We have some room here , it is not possible to have more than 12 mip levels on PS3 and 16 on PC is pushing it .
* 54 - 56 : Type ( Max 3 )
* 56 - 57 : Sharing ( Max 1 ) < - Boolean . Exclusive = 0 , shared = 1
* 57 - 64 : Flags ( Max 127 ) < - We have some room here , we only care about a small subset of create flags .
*/
2022-02-06 12:23:46 +01:00
ensure ( static_cast < u32 > ( format ) < 0xFF ) ;
return ( static_cast < u64 > ( format ) & 0xFF ) |
2025-04-05 21:50:45 +02:00
( static_cast < u64 > ( w ) < < 8 ) |
( static_cast < u64 > ( h ) < < 24 ) |
( static_cast < u64 > ( d ) < < 40 ) |
( static_cast < u64 > ( mipmaps ) < < 48 ) |
( static_cast < u64 > ( type ) < < 54 ) |
( static_cast < u64 > ( sharing_mode ) < < 56 ) |
( static_cast < u64 > ( create_flags ) < < 57 ) ;
2022-02-06 12:23:46 +01:00
}
2022-02-05 19:11:56 +01:00
texture_cache : : cached_image_reference_t : : cached_image_reference_t ( texture_cache * parent , std : : unique_ptr < vk : : viewable_image > & previous )
{
2022-02-05 20:26:42 +01:00
ensure ( previous ) ;
2022-02-05 19:11:56 +01:00
this - > parent = parent ;
this - > data = std : : move ( previous ) ;
}
2022-02-08 22:44:17 +01:00
texture_cache : : cached_image_reference_t : : ~ cached_image_reference_t ( )
2022-02-05 19:11:56 +01:00
{
// Erase layout information to force TOP_OF_PIPE transition next time.
data - > current_layout = VK_IMAGE_LAYOUT_UNDEFINED ;
2022-02-08 20:49:09 +01:00
data - > current_queue_family = VK_QUEUE_FAMILY_IGNORED ;
2022-02-05 19:11:56 +01:00
// Move this object to the cached image pool
2022-07-24 19:28:57 +02:00
const auto key = hash_image_properties ( data - > format ( ) , data - > width ( ) , data - > height ( ) , data - > depth ( ) , data - > mipmaps ( ) , data - > info . imageType , data - > info . flags , data - > info . sharingMode ) ;
2022-02-05 19:11:56 +01:00
std : : lock_guard lock ( parent - > m_cached_pool_lock ) ;
2022-02-08 20:49:09 +01:00
if ( ! parent - > m_cache_is_exiting )
{
parent - > m_cached_memory_size + = data - > memory - > size ( ) ;
parent - > m_cached_images . emplace_front ( key , data ) ;
}
else
{
// Destroy if the cache is closed. The GPU is done with this resource anyway.
data . reset ( ) ;
}
2022-02-05 19:11:56 +01:00
}
2020-12-18 15:43:34 +01:00
void cached_texture_section : : dma_transfer ( vk : : command_buffer & cmd , vk : : image * src , const areai & src_area , const utils : : address_range & valid_range , u32 pitch )
{
ensure ( src - > samples ( ) = = 1 ) ;
if ( ! m_device )
{
m_device = & cmd . get_command_pool ( ) . get_owner ( ) ;
}
if ( dma_fence )
{
// NOTE: This can be reached if previously synchronized, or a special path happens.
// If a hard flush occurred while this surface was flush_always the cache would have reset its protection afterwards.
// DMA resource would still be present but already used to flush previously.
vk : : get_resource_manager ( ) - > dispose ( dma_fence ) ;
}
if ( vk : : is_renderpass_open ( cmd ) )
{
vk : : end_renderpass ( cmd ) ;
}
src - > push_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) ;
const auto internal_bpp = vk : : get_format_texel_width ( src - > format ( ) ) ;
const auto transfer_width = static_cast < u32 > ( src_area . width ( ) ) ;
const auto transfer_height = static_cast < u32 > ( src_area . height ( ) ) ;
real_pitch = internal_bpp * transfer_width ;
rsx_pitch = pitch ;
const bool require_format_conversion = ! ! ( src - > aspect ( ) & VK_IMAGE_ASPECT_STENCIL_BIT ) | | src - > format ( ) = = VK_FORMAT_D32_SFLOAT ;
2023-09-12 01:41:25 +02:00
const auto tiled_region = rsx : : get_current_renderer ( ) - > get_tiled_memory_region ( valid_range ) ;
const bool require_tiling = ! ! tiled_region ;
const bool require_gpu_transform = require_format_conversion | | pack_unpack_swap_bytes | | require_tiling ;
2024-02-05 23:29:51 +01:00
auto dma_sync_region = valid_range ;
2025-04-05 21:50:45 +02:00
dma_mapping_handle dma_mapping = { 0 , nullptr } ;
2024-02-05 23:29:51 +01:00
auto dma_sync = [ & dma_sync_region , & dma_mapping ] ( bool load , bool force = false )
{
if ( dma_mapping . second & & ! force )
{
return ;
}
dma_mapping = vk : : map_dma ( dma_sync_region . start , dma_sync_region . length ( ) ) ;
if ( load )
{
vk : : load_dma ( dma_sync_region . start , dma_sync_region . length ( ) ) ;
}
} ;
2023-06-26 22:01:54 +02:00
2023-09-12 01:41:25 +02:00
if ( require_gpu_transform )
2020-12-18 15:43:34 +01:00
{
const auto transfer_pitch = real_pitch ;
const auto task_length = transfer_pitch * src_area . height ( ) ;
2023-09-12 01:41:25 +02:00
auto working_buffer_length = calculate_working_buffer_size ( task_length , src - > aspect ( ) ) ;
2024-02-05 23:29:51 +01:00
# if !DEBUG_DMA_TILING
2023-09-12 02:38:59 +02:00
if ( require_tiling )
{
2024-02-05 23:29:51 +01:00
// Safety padding
2023-09-12 01:41:25 +02:00
working_buffer_length + = tiled_region . tile - > size ;
2023-09-12 02:38:59 +02:00
2024-02-05 23:29:51 +01:00
// Calculate actual working section for the memory op
dma_sync_region = tiled_region . tile_align ( dma_sync_region ) ;
2023-09-12 01:41:25 +02:00
}
2024-02-05 23:29:51 +01:00
# endif
2020-12-18 15:43:34 +01:00
2022-02-12 21:06:50 +01:00
auto working_buffer = vk : : get_scratch_buffer ( cmd , working_buffer_length ) ;
2023-09-12 01:41:25 +02:00
u32 result_offset = 0 ;
2020-12-18 15:43:34 +01:00
VkBufferImageCopy region = { } ;
2025-04-05 21:50:45 +02:00
region . imageSubresource = { src - > aspect ( ) , 0 , 0 , 1 } ;
region . imageOffset = { src_area . x1 , src_area . y1 , 0 } ;
region . imageExtent = { transfer_width , transfer_height , 1 } ;
2023-06-26 22:01:54 +02:00
bool require_rw_barrier = true ;
image_readback_options_t xfer_options { } ;
xfer_options . swap_bytes = require_format_conversion & & pack_unpack_swap_bytes ;
vk : : copy_image_to_buffer ( cmd , src , working_buffer , region , xfer_options ) ;
2020-12-18 15:43:34 +01:00
// NOTE: For depth/stencil formats, copying to buffer and byteswap are combined into one step above
if ( pack_unpack_swap_bytes & & ! require_format_conversion )
{
const auto texel_layout = vk : : get_format_element_size ( src - > format ( ) ) ;
const auto elem_size = texel_layout . first ;
2021-07-14 00:25:32 +02:00
vk : : cs_shuffle_base * shuffle_kernel ;
2020-12-18 15:43:34 +01:00
if ( elem_size = = 2 )
{
shuffle_kernel = vk : : get_compute_task < vk : : cs_shuffle_16 > ( ) ;
}
else if ( elem_size = = 4 )
{
shuffle_kernel = vk : : get_compute_task < vk : : cs_shuffle_32 > ( ) ;
}
else
{
ensure ( get_context ( ) = = rsx : : texture_upload_context : : dma ) ;
shuffle_kernel = nullptr ;
}
if ( shuffle_kernel )
{
vk : : insert_buffer_memory_barrier ( cmd , working_buffer - > value , 0 , task_length ,
2023-06-26 22:01:54 +02:00
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ,
VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT , VK_ACCESS_SHADER_READ_BIT ) ;
2020-12-18 15:43:34 +01:00
shuffle_kernel - > run ( cmd , working_buffer , task_length ) ;
2023-09-12 01:41:25 +02:00
if ( ! require_tiling )
{
vk : : insert_buffer_memory_barrier ( cmd , working_buffer - > value , 0 , task_length ,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT , VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_ACCESS_SHADER_WRITE_BIT , VK_ACCESS_TRANSFER_READ_BIT ) ;
2023-06-26 22:01:54 +02:00
2023-09-12 01:41:25 +02:00
require_rw_barrier = false ;
}
2020-12-18 15:43:34 +01:00
}
}
2023-09-12 01:41:25 +02:00
if ( require_tiling )
{
# if !DEBUG_DMA_TILING
// Compute -> Compute barrier
vk : : insert_buffer_memory_barrier ( cmd , working_buffer - > value , 0 , task_length ,
2024-02-05 23:29:51 +01:00
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ,
2024-02-06 01:20:43 +01:00
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT , VK_ACCESS_SHADER_READ_BIT ) ;
2024-02-05 23:29:51 +01:00
// We don't need to calibrate write if two conditions are met:
// 1. The start offset of our 2D region is a multiple of 64 lines
// 2. We use the whole pitch.
// If these conditions are not met, we need to upload the entire tile (or at least the affected tiles wholly)
2024-02-06 01:20:43 +01:00
// FIXME: There is a 3rd condition - write onto already-persisted range. e.g One transfer copies half the image then the other half is copied later.
// We don't need to load again for the second copy in that scenario.
2024-02-05 23:29:51 +01:00
if ( valid_range . start ! = dma_sync_region . start | | real_pitch ! = tiled_region . tile - > pitch )
{
// Tile indices run to the end of the row (full pitch).
// Tiles address outside their 64x64 area too, so we need to actually load the whole thing and "fill in" missing blocks.
// Visualizing "hot" pixels when doing a partial copy is very revealing, there's lots of data from the padding areas to be filled in.
dma_sync ( true ) ;
ensure ( dma_mapping . second ) ;
// Upload memory to the working buffer
const auto dst_offset = task_length ; // Append to the end of the input
VkBufferCopy mem_load { } ;
mem_load . srcOffset = dma_mapping . first ;
mem_load . dstOffset = dst_offset ;
mem_load . size = dma_sync_region . length ( ) ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdCopyBuffer ) ( cmd , dma_mapping . second - > value , working_buffer - > value , 1 , & mem_load ) ;
2024-02-05 23:29:51 +01:00
// Transfer -> Compute barrier
vk : : insert_buffer_memory_barrier ( cmd , working_buffer - > value , dst_offset , dma_sync_region . length ( ) ,
VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ,
VK_ACCESS_SHADER_WRITE_BIT , VK_ACCESS_SHADER_WRITE_BIT ) ;
}
2023-09-12 01:41:25 +02:00
// Prepare payload
const RSX_detiler_config config =
2025-04-05 21:50:45 +02:00
{
. tile_base_address = tiled_region . base_address ,
. tile_base_offset = valid_range . start - tiled_region . base_address ,
. tile_rw_offset = dma_sync_region . start - tiled_region . base_address ,
. tile_size = tiled_region . tile - > size ,
. tile_pitch = tiled_region . tile - > pitch ,
. bank = tiled_region . tile - > bank ,
. dst = working_buffer ,
. dst_offset = task_length ,
. src = working_buffer ,
. src_offset = 0 ,
// TODO: Check interaction with anti-aliasing
. image_width = static_cast < u16 > ( transfer_width ) ,
. image_height = static_cast < u16 > ( transfer_height ) ,
. image_pitch = real_pitch ,
. image_bpp = context = = rsx : : texture_upload_context : : dma ? internal_bpp : rsx : : get_format_block_size_in_bytes ( gcm_format ) } ;
2023-09-12 01:41:25 +02:00
// Execute
const auto job = vk : : get_compute_task < vk : : cs_tile_memcpy < RSX_detiler_op : : encode > > ( ) ;
job - > run ( cmd , config ) ;
2023-09-12 02:38:59 +02:00
// Update internal variables
2023-09-12 01:41:25 +02:00
result_offset = task_length ;
2024-02-05 23:29:51 +01:00
real_pitch = tiled_region . tile - > pitch ; // We're always copying the full image. In case of partials we're "filling in" blocks, not doing partial 2D copies.
2023-09-12 01:41:25 +02:00
require_rw_barrier = true ;
2024-02-05 23:29:51 +01:00
2024-02-06 00:07:04 +01:00
# if VISUALIZE_GPU_TILING
if ( g_cfg . video . renderdoc_compatiblity )
{
vk : : insert_buffer_memory_barrier ( cmd , working_buffer - > value , result_offset , working_buffer_length ,
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ,
VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT , VK_ACCESS_SHADER_READ_BIT ) ;
2024-02-05 23:29:51 +01:00
2024-02-06 00:07:04 +01:00
// Debug write
auto scratch_img = vk : : get_typeless_helper ( VK_FORMAT_B8G8R8A8_UNORM , RSX_FORMAT_CLASS_COLOR , tiled_region . tile - > pitch / 4 , 768 ) ;
scratch_img - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
2024-02-05 23:29:51 +01:00
2024-02-06 00:07:04 +01:00
VkBufferImageCopy dbg_copy { } ;
dbg_copy . bufferOffset = config . dst_offset ;
dbg_copy . imageExtent . width = width ;
dbg_copy . imageExtent . height = height ;
dbg_copy . imageExtent . depth = 1 ;
dbg_copy . bufferRowLength = tiled_region . tile - > pitch / 4 ;
2025-04-05 21:50:45 +02:00
dbg_copy . imageSubresource = { . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT , . mipLevel = 0 , . baseArrayLayer = 0 , . layerCount = 1 } ;
2024-02-06 00:07:04 +01:00
vk : : copy_buffer_to_image ( cmd , working_buffer , scratch_img , dbg_copy ) ;
2024-02-05 23:29:51 +01:00
2024-02-06 00:07:04 +01:00
scratch_img - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) ;
}
2024-02-05 23:29:51 +01:00
# endif
2023-09-12 01:41:25 +02:00
# endif
}
2023-06-26 22:01:54 +02:00
if ( require_rw_barrier )
{
2024-02-06 00:07:04 +01:00
vk : : insert_buffer_memory_barrier ( cmd , working_buffer - > value , result_offset , dma_sync_region . length ( ) ,
2023-06-26 22:01:54 +02:00
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ,
VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT , VK_ACCESS_SHADER_READ_BIT ) ;
}
2020-12-18 15:43:34 +01:00
if ( rsx_pitch = = real_pitch ) [[likely]]
{
2024-02-05 23:29:51 +01:00
dma_sync ( false ) ;
2020-12-18 15:43:34 +01:00
VkBufferCopy copy = { } ;
2023-09-12 01:41:25 +02:00
copy . srcOffset = result_offset ;
2023-06-26 22:01:54 +02:00
copy . dstOffset = dma_mapping . first ;
2024-02-06 00:07:04 +01:00
copy . size = dma_sync_region . length ( ) ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdCopyBuffer ) ( cmd , working_buffer - > value , dma_mapping . second - > value , 1 , & copy ) ;
2020-12-18 15:43:34 +01:00
}
else
{
2024-02-05 23:29:51 +01:00
dma_sync ( true ) ;
2020-12-18 15:43:34 +01:00
std : : vector < VkBufferCopy > copy ;
copy . reserve ( transfer_height ) ;
2023-06-26 22:01:54 +02:00
u32 dst_offset = dma_mapping . first ;
2023-09-12 01:41:25 +02:00
u32 src_offset = result_offset ;
2020-12-18 15:43:34 +01:00
for ( unsigned row = 0 ; row < transfer_height ; + + row )
{
2025-04-05 21:50:45 +02:00
copy . push_back ( { src_offset , dst_offset , transfer_pitch } ) ;
2020-12-18 15:43:34 +01:00
src_offset + = real_pitch ;
dst_offset + = rsx_pitch ;
}
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdCopyBuffer ) ( cmd , working_buffer - > value , dma_mapping . second - > value , transfer_height , copy . data ( ) ) ;
2020-12-18 15:43:34 +01:00
}
}
else
{
2024-02-05 23:29:51 +01:00
dma_sync ( false ) ;
2020-12-18 15:43:34 +01:00
VkBufferImageCopy region = { } ;
region . bufferRowLength = ( rsx_pitch / internal_bpp ) ;
2025-04-05 21:50:45 +02:00
region . imageSubresource = { src - > aspect ( ) , 0 , 0 , 1 } ;
region . imageOffset = { src_area . x1 , src_area . y1 , 0 } ;
region . imageExtent = { transfer_width , transfer_height , 1 } ;
2020-12-18 15:43:34 +01:00
2023-06-26 22:01:54 +02:00
region . bufferOffset = dma_mapping . first ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdCopyImageToBuffer ) ( cmd , src - > value , src - > current_layout , dma_mapping . second - > value , 1 , & region ) ;
2020-12-18 15:43:34 +01:00
}
src - > pop_layout ( cmd ) ;
2023-06-28 16:08:06 +02:00
VkBufferMemoryBarrier2KHR mem_barrier =
2025-04-05 21:50:45 +02:00
{
. sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2_KHR ,
. srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT_KHR , // Finish all transfer...
. srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR ,
. dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR , // ...before proceeding with any command
. dstAccessMask = 0 ,
. buffer = dma_mapping . second - > value ,
. offset = dma_mapping . first ,
. size = valid_range . length ( ) } ;
2023-06-21 23:19:03 +02:00
2020-12-18 15:43:34 +01:00
// Create event object for this transfer and queue signal op
2023-06-30 01:38:08 +02:00
dma_fence = std : : make_unique < vk : : event > ( * m_device , sync_domain : : host ) ;
2023-06-21 23:19:03 +02:00
dma_fence - > signal ( cmd ,
2025-04-05 21:50:45 +02:00
{ . sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO ,
. bufferMemoryBarrierCount = 1 ,
. pBufferMemoryBarriers = & mem_barrier } ) ;
2020-12-18 15:43:34 +01:00
// Set cb flag for queued dma operations
cmd . set_flag ( vk : : command_buffer : : cb_has_dma_transfer ) ;
if ( get_context ( ) = = rsx : : texture_upload_context : : dma )
{
// Save readback hint in case transformation is required later
switch ( internal_bpp )
{
case 2 :
gcm_format = CELL_GCM_TEXTURE_R5G6B5 ;
break ;
case 4 :
default :
gcm_format = CELL_GCM_TEXTURE_A8R8G8B8 ;
break ;
}
}
synchronized = true ;
2021-09-17 21:59:37 +02:00
sync_timestamp = rsx : : get_shared_tag ( ) ;
2020-12-18 15:43:34 +01:00
}
2021-07-14 00:25:32 +02:00
void texture_cache : : on_section_destroyed ( cached_texture_section & tex )
{
2022-02-05 20:26:42 +01:00
if ( tex . is_managed ( ) & & tex . exists ( ) )
2021-07-14 00:25:32 +02:00
{
2022-02-08 22:44:17 +01:00
auto disposable = vk : : disposable_t : : make ( new cached_image_reference_t ( this , tex . get_texture ( ) ) ) ;
2022-02-05 19:11:56 +01:00
vk : : get_resource_manager ( ) - > dispose ( disposable ) ;
2021-07-14 00:25:32 +02:00
}
}
void texture_cache : : clear ( )
{
2022-02-08 20:49:09 +01:00
{
std : : lock_guard lock ( m_cached_pool_lock ) ;
m_cache_is_exiting = true ;
}
2021-07-14 00:25:32 +02:00
baseclass : : clear ( ) ;
2022-02-05 19:11:56 +01:00
m_cached_images . clear ( ) ;
m_cached_memory_size = 0 ;
2021-07-14 00:25:32 +02:00
}
2020-12-18 15:43:34 +01:00
void texture_cache : : copy_transfer_regions_impl ( vk : : command_buffer & cmd , vk : : image * dst , const std : : vector < copy_region_descriptor > & sections_to_transfer ) const
{
const auto dst_aspect = dst - > aspect ( ) ;
const auto dst_bpp = vk : : get_format_texel_width ( dst - > format ( ) ) ;
2021-07-14 00:25:32 +02:00
for ( const auto & section : sections_to_transfer )
2020-12-18 15:43:34 +01:00
{
if ( ! section . src )
2023-08-07 22:30:12 +02:00
{
2020-12-18 15:43:34 +01:00
continue ;
2023-08-07 22:30:12 +02:00
}
// Generates a region to write data to the final destination
const auto get_output_region = [ & ] ( s32 in_x , s32 in_y , u32 w , u32 h , vk : : image * data_src )
{
VkImageCopy copy_rgn = {
2025-04-05 21:50:45 +02:00
. srcSubresource = { data_src - > aspect ( ) , 0 , 0 , 1 } ,
. srcOffset = { in_x , in_y , 0 } ,
. dstSubresource = { dst_aspect , section . level , 0 , 1 } ,
. dstOffset = { section . dst_x , section . dst_y , 0 } ,
. extent = { w , h , 1 } } ;
2023-08-07 22:30:12 +02:00
if ( dst - > info . imageType = = VK_IMAGE_TYPE_3D )
{
copy_rgn . dstOffset . z = section . dst_z ;
}
else
{
copy_rgn . dstSubresource . baseArrayLayer = section . dst_z ;
}
return copy_rgn ;
} ;
2020-12-18 15:43:34 +01:00
const bool typeless = section . src - > aspect ( ) ! = dst_aspect | |
2025-04-05 21:50:45 +02:00
! formats_are_bitcast_compatible ( dst , section . src ) ;
2020-12-18 15:43:34 +01:00
// Avoid inserting unnecessary barrier GENERAL->TRANSFER_SRC->GENERAL in active render targets
const auto preferred_layout = ( section . src - > current_layout ! = VK_IMAGE_LAYOUT_GENERAL ) ?
2025-04-05 21:50:45 +02:00
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
VK_IMAGE_LAYOUT_GENERAL ;
2020-12-18 15:43:34 +01:00
section . src - > push_layout ( cmd , preferred_layout ) ;
auto src_image = section . src ;
auto src_x = section . src_x ;
auto src_y = section . src_y ;
auto src_w = section . src_w ;
auto src_h = section . src_h ;
rsx : : flags32_t transform = section . xform ;
if ( section . xform = = rsx : : surface_transform : : coordinate_transform )
{
// Dimensions were given in 'dst' space. Work out the real source coordinates
const auto src_bpp = vk : : get_format_texel_width ( section . src - > format ( ) ) ;
src_x = ( src_x * dst_bpp ) / src_bpp ;
2025-10-05 18:28:03 +02:00
src_w = rx : : aligned_div < u16 > ( src_w * dst_bpp , src_bpp ) ;
2020-12-18 15:43:34 +01:00
transform & = ~ ( rsx : : surface_transform : : coordinate_transform ) ;
}
if ( auto surface = dynamic_cast < vk : : render_target * > ( section . src ) )
{
surface - > transform_samples_to_pixels ( src_x , src_w , src_y , src_h ) ;
}
if ( typeless ) [[unlikely]]
{
const auto src_bpp = vk : : get_format_texel_width ( section . src - > format ( ) ) ;
const u16 convert_w = u16 ( src_w * src_bpp ) / dst_bpp ;
const u16 convert_x = u16 ( src_x * src_bpp ) / dst_bpp ;
if ( convert_w = = section . dst_w & & src_h = = section . dst_h & &
transform = = rsx : : surface_transform : : identity & &
section . level = = 0 & & section . dst_z = = 0 )
{
// Optimization to avoid double transfer
// TODO: Handle level and layer offsets
2025-04-05 21:50:45 +02:00
const areai src_rect = coordi { { src_x , src_y } , { src_w , src_h } } ;
const areai dst_rect = coordi { { section . dst_x , section . dst_y } , { section . dst_w , section . dst_h } } ;
2020-12-18 15:43:34 +01:00
vk : : copy_image_typeless ( cmd , section . src , dst , src_rect , dst_rect , 1 ) ;
section . src - > pop_layout ( cmd ) ;
continue ;
}
src_image = vk : : get_typeless_helper ( dst - > format ( ) , dst - > format_class ( ) , convert_x + convert_w , src_y + src_h ) ;
src_image - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
2025-04-05 21:50:45 +02:00
const areai src_rect = coordi { { src_x , src_y } , { src_w , src_h } } ;
const areai dst_rect = coordi { { 0 , 0 } , { convert_w , src_h } } ;
2020-12-18 15:43:34 +01:00
vk : : copy_image_typeless ( cmd , section . src , src_image , src_rect , dst_rect , 1 ) ;
src_image - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) ;
2023-08-07 22:30:12 +02:00
src_x = 0 ;
src_y = 0 ;
2020-12-18 15:43:34 +01:00
src_w = convert_w ;
}
ensure ( src_image - > current_layout = = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL | | src_image - > current_layout = = VK_IMAGE_LAYOUT_GENERAL ) ;
2023-08-07 22:41:27 +02:00
ensure ( transform = = rsx : : surface_transform : : identity ) ;
2020-12-18 15:43:34 +01:00
2023-08-07 22:41:27 +02:00
if ( src_w = = section . dst_w & & src_h = = section . dst_h ) [[likely]]
2020-12-18 15:43:34 +01:00
{
2023-08-07 22:30:12 +02:00
const auto copy_rgn = get_output_region ( src_x , src_y , src_w , src_h , src_image ) ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdCopyImage ) ( cmd , src_image - > value , src_image - > current_layout , dst - > value , dst - > current_layout , 1 , & copy_rgn ) ;
2020-12-18 15:43:34 +01:00
}
else
{
u16 dst_x = section . dst_x , dst_y = section . dst_y ;
2023-08-07 22:41:27 +02:00
vk : : image * _dst = dst ;
2020-12-18 15:43:34 +01:00
2025-04-05 21:50:45 +02:00
if ( src_image - > info . format ! = dst - > info . format | | section . level ! = 0 | | section . dst_z ! = 0 ) [[unlikely]]
2020-12-18 15:43:34 +01:00
{
2023-08-07 22:41:27 +02:00
// Either a bitcast is required or a scale+copy to mipmap level / layer
2023-08-07 22:30:12 +02:00
const u32 requested_width = dst - > width ( ) ;
2023-09-19 02:31:07 +02:00
const u32 requested_height = src_y + src_h + section . dst_h ; // Accounts for possible typeless ref on the same helper on src
2023-08-07 22:30:12 +02:00
_dst = vk : : get_typeless_helper ( src_image - > format ( ) , src_image - > format_class ( ) , requested_width , requested_height ) ;
2020-12-18 15:43:34 +01:00
_dst - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
}
2023-09-19 02:31:07 +02:00
if ( _dst ! = dst )
2023-08-07 22:30:12 +02:00
{
2023-09-19 02:31:07 +02:00
// We place the output after the source to account for the initial typeless-xfer if applicable
// If src_image == _dst then this is just a write-to-self. Either way, use best-fit placement.
2023-08-07 22:30:12 +02:00
dst_x = 0 ;
dst_y = src_y + src_h ;
}
2023-08-07 22:41:27 +02:00
vk : : copy_scaled_image ( cmd , src_image , _dst ,
2025-04-05 21:50:45 +02:00
coordi { { src_x , src_y } , { src_w , src_h } } ,
coordi { { dst_x , dst_y } , { section . dst_w , section . dst_h } } ,
2023-08-07 22:41:27 +02:00
1 , src_image - > format ( ) = = _dst - > format ( ) ,
VK_FILTER_NEAREST ) ;
2020-12-18 15:43:34 +01:00
if ( _dst ! = dst ) [[unlikely]]
{
// Casting comes after the scaling!
2023-08-07 22:30:12 +02:00
const auto copy_rgn = get_output_region ( dst_x , dst_y , section . dst_w , section . dst_h , _dst ) ;
2020-12-18 15:43:34 +01:00
_dst - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdCopyImage ) ( cmd , _dst - > value , _dst - > current_layout , dst - > value , dst - > current_layout , 1 , & copy_rgn ) ;
2020-12-18 15:43:34 +01:00
}
}
section . src - > pop_layout ( cmd ) ;
}
}
2021-07-14 00:25:32 +02:00
VkComponentMapping texture_cache : : apply_component_mapping_flags ( u32 gcm_format , rsx : : component_order flags , const rsx : : texture_channel_remap_t & remap_vector ) const
{
switch ( gcm_format )
{
case CELL_GCM_TEXTURE_DEPTH24_D8 :
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT :
case CELL_GCM_TEXTURE_DEPTH16 :
case CELL_GCM_TEXTURE_DEPTH16_FLOAT :
2021-07-14 19:16:57 +02:00
// Dont bother letting this propagate
2025-04-05 21:50:45 +02:00
return { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_R } ;
2021-07-14 00:25:32 +02:00
default :
break ;
}
VkComponentMapping mapping = { } ;
switch ( flags )
{
case rsx : : component_order : : default_ :
{
mapping = vk : : apply_swizzle_remap ( vk : : get_component_mapping ( gcm_format ) , remap_vector ) ;
break ;
}
case rsx : : component_order : : native :
{
2025-04-05 21:50:45 +02:00
mapping = { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B , VK_COMPONENT_SWIZZLE_A } ;
2021-07-14 00:25:32 +02:00
break ;
}
case rsx : : component_order : : swapped_native :
{
2025-04-05 21:50:45 +02:00
mapping = { VK_COMPONENT_SWIZZLE_A , VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B } ;
2021-07-14 00:25:32 +02:00
break ;
}
default :
break ;
}
return mapping ;
}
vk : : image * texture_cache : : get_template_from_collection_impl ( const std : : vector < copy_region_descriptor > & sections_to_transfer ) const
{
if ( sections_to_transfer . size ( ) = = 1 ) [[likely]]
{
return sections_to_transfer . front ( ) . src ;
}
vk : : image * result = nullptr ;
for ( const auto & section : sections_to_transfer )
{
if ( ! section . src )
continue ;
if ( ! result )
{
result = section . src ;
}
else
{
if ( section . src - > native_component_map . a ! = result - > native_component_map . a | |
section . src - > native_component_map . r ! = result - > native_component_map . r | |
section . src - > native_component_map . g ! = result - > native_component_map . g | |
section . src - > native_component_map . b ! = result - > native_component_map . b )
{
// TODO
// This requires a far more complex setup as its not always possible to mix and match without compute assistance
return nullptr ;
}
}
}
return result ;
}
2022-07-24 19:28:57 +02:00
std : : unique_ptr < vk : : viewable_image > texture_cache : : find_cached_image ( VkFormat format , u16 w , u16 h , u16 d , u16 mipmaps , VkImageType type , VkImageCreateFlags create_flags , VkImageUsageFlags usage , VkSharingMode sharing )
2021-07-14 00:25:32 +02:00
{
2022-02-05 19:11:56 +01:00
reader_lock lock ( m_cached_pool_lock ) ;
2021-07-14 00:25:32 +02:00
2022-02-05 19:11:56 +01:00
if ( ! m_cached_images . empty ( ) )
2021-07-14 00:25:32 +02:00
{
2022-07-24 19:28:57 +02:00
const u64 desired_key = hash_image_properties ( format , w , h , d , mipmaps , type , create_flags , sharing ) ;
2022-02-05 19:11:56 +01:00
lock . upgrade ( ) ;
for ( auto it = m_cached_images . begin ( ) ; it ! = m_cached_images . end ( ) ; + + it )
2021-07-14 00:25:32 +02:00
{
2022-02-08 20:49:09 +01:00
if ( it - > key = = desired_key & & ( it - > data - > info . usage & usage ) = = usage )
2022-02-05 19:11:56 +01:00
{
2022-02-06 12:23:46 +01:00
auto ret = std : : move ( it - > data ) ;
2022-02-05 19:11:56 +01:00
m_cached_images . erase ( it ) ;
m_cached_memory_size - = ret - > memory - > size ( ) ;
return ret ;
}
2021-07-14 00:25:32 +02:00
}
}
return { } ;
}
2024-01-04 01:24:23 +01:00
std : : unique_ptr < vk : : viewable_image > texture_cache : : create_temporary_subresource_storage (
rsx : : format_class format_class , VkFormat format ,
u16 width , u16 height , u16 depth , u16 layers , u8 mips ,
VkImageType image_type , VkFlags image_flags , VkFlags usage_flags )
2021-07-14 00:25:32 +02:00
{
2024-01-04 01:24:23 +01:00
auto image = find_cached_image ( format , width , height , depth , mips , image_type , image_flags , usage_flags , VK_SHARING_MODE_EXCLUSIVE ) ;
2021-07-14 00:25:32 +02:00
if ( ! image )
{
image = std : : make_unique < vk : : viewable_image > ( * vk : : get_current_renderer ( ) , m_memory_types . device_local , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
image_type ,
2024-01-04 01:24:23 +01:00
format ,
width , height , depth , mips , layers , VK_SAMPLE_COUNT_1_BIT , VK_IMAGE_LAYOUT_UNDEFINED ,
2022-07-24 19:28:57 +02:00
VK_IMAGE_TILING_OPTIMAL , VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT , image_flags | VK_IMAGE_CREATE_ALLOW_NULL_RPCS3 ,
2024-01-04 01:24:23 +01:00
VMM_ALLOCATION_POOL_TEXTURE_CACHE , format_class ) ;
2021-07-26 20:12:05 +02:00
if ( ! image - > value )
{
// OOM, bail
return nullptr ;
}
2021-07-14 00:25:32 +02:00
}
2024-01-04 01:24:23 +01:00
return image ;
}
void texture_cache : : dispose_reusable_image ( std : : unique_ptr < vk : : viewable_image > & image )
{
auto disposable = vk : : disposable_t : : make ( new cached_image_reference_t ( this , image ) ) ;
vk : : get_resource_manager ( ) - > dispose ( disposable ) ;
}
vk : : image_view * texture_cache : : create_temporary_subresource_view_impl ( vk : : command_buffer & cmd , vk : : image * source , VkImageType image_type , VkImageViewType view_type ,
u32 gcm_format , u16 x , u16 y , u16 w , u16 h , u16 d , u8 mips , const rsx : : texture_channel_remap_t & remap_vector , bool copy )
{
const VkImageCreateFlags image_flags = ( view_type = = VK_IMAGE_VIEW_TYPE_CUBE ) ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0 ;
const VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
const VkFormat dst_format = vk : : get_compatible_sampler_format ( m_formats_support , gcm_format ) ;
const u16 layers = ( view_type = = VK_IMAGE_VIEW_TYPE_CUBE ) ? 6 : 1 ;
// Provision
auto image = create_temporary_subresource_storage ( rsx : : classify_format ( gcm_format ) , dst_format , w , h , d , layers , mips , image_type , image_flags , usage_flags ) ;
// OOM?
if ( ! image )
{
return nullptr ;
}
2021-07-14 19:16:57 +02:00
// This method is almost exclusively used to work on framebuffer resources
// Keep the original swizzle layout unless there is data format conversion
2021-07-14 00:25:32 +02:00
VkComponentMapping view_swizzle ;
if ( ! source | | dst_format ! = source - > info . format )
{
// This is a data cast operation
// Use native mapping for the new type
// TODO: Also simulate the readback+reupload step (very tricky)
const auto remap = get_component_mapping ( gcm_format ) ;
2025-04-05 21:50:45 +02:00
view_swizzle = { remap [ 1 ] , remap [ 2 ] , remap [ 3 ] , remap [ 0 ] } ;
2021-07-14 00:25:32 +02:00
}
else
{
view_swizzle = source - > native_component_map ;
}
2022-08-05 19:05:04 +02:00
image - > set_debug_name ( " Temp view " ) ;
2021-07-14 00:25:32 +02:00
image - > set_native_component_layout ( view_swizzle ) ;
2024-10-03 01:59:42 +02:00
auto view = image - > get_view ( remap_vector ) ;
2021-07-14 00:25:32 +02:00
if ( copy )
{
std : : vector < copy_region_descriptor > region =
2025-04-05 21:50:45 +02:00
{ { . src = source ,
. xform = rsx : : surface_transform : : coordinate_transform ,
. src_x = x ,
. src_y = y ,
. src_w = w ,
. src_h = h ,
. dst_w = w ,
. dst_h = h } } ;
2021-07-14 00:25:32 +02:00
vk : : change_image_layout ( cmd , image . get ( ) , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
copy_transfer_regions_impl ( cmd , image . get ( ) , region ) ;
vk : : change_image_layout ( cmd , image . get ( ) , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) ;
}
2022-02-05 19:11:56 +01:00
// TODO: Floating reference. We can do better with some restructuring.
image . release ( ) ;
2021-07-14 00:25:32 +02:00
return view ;
}
vk : : image_view * texture_cache : : create_temporary_subresource_view ( vk : : command_buffer & cmd , vk : : image * source , u32 gcm_format ,
u16 x , u16 y , u16 w , u16 h , const rsx : : texture_channel_remap_t & remap_vector )
{
return create_temporary_subresource_view_impl ( cmd , source , source - > info . imageType , VK_IMAGE_VIEW_TYPE_2D ,
gcm_format , x , y , w , h , 1 , 1 , remap_vector , true ) ;
}
vk : : image_view * texture_cache : : create_temporary_subresource_view ( vk : : command_buffer & cmd , vk : : image * * source , u32 gcm_format ,
u16 x , u16 y , u16 w , u16 h , const rsx : : texture_channel_remap_t & remap_vector )
{
return create_temporary_subresource_view ( cmd , * source , gcm_format , x , y , w , h , remap_vector ) ;
}
vk : : image_view * texture_cache : : generate_cubemap_from_images ( vk : : command_buffer & cmd , u32 gcm_format , u16 size ,
const std : : vector < copy_region_descriptor > & sections_to_copy , const rsx : : texture_channel_remap_t & remap_vector )
{
auto _template = get_template_from_collection_impl ( sections_to_copy ) ;
auto result = create_temporary_subresource_view_impl ( cmd , _template , VK_IMAGE_TYPE_2D ,
VK_IMAGE_VIEW_TYPE_CUBE , gcm_format , 0 , 0 , size , size , 1 , 1 , remap_vector , false ) ;
2021-07-26 20:12:05 +02:00
if ( ! result )
{
// Failed to create temporary object, bail
return nullptr ;
}
2021-07-14 00:25:32 +02:00
const auto image = result - > image ( ) ;
VkImageAspectFlags dst_aspect = vk : : get_aspect_flags ( result - > info . format ) ;
2025-04-05 21:50:45 +02:00
VkImageSubresourceRange dst_range = { dst_aspect , 0 , 1 , 0 , 6 } ;
2021-07-14 00:25:32 +02:00
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , dst_range ) ;
if ( ! ( dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT ) )
{
VkClearColorValue clear = { } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearColorImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2021-07-14 00:25:32 +02:00
}
else
{
2025-04-05 21:50:45 +02:00
VkClearDepthStencilValue clear = { 1.f , 0 } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearDepthStencilImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2021-07-14 00:25:32 +02:00
}
copy_transfer_regions_impl ( cmd , image , sections_to_copy ) ;
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , dst_range ) ;
return result ;
}
vk : : image_view * texture_cache : : generate_3d_from_2d_images ( vk : : command_buffer & cmd , u32 gcm_format , u16 width , u16 height , u16 depth ,
const std : : vector < copy_region_descriptor > & sections_to_copy , const rsx : : texture_channel_remap_t & remap_vector )
{
auto _template = get_template_from_collection_impl ( sections_to_copy ) ;
auto result = create_temporary_subresource_view_impl ( cmd , _template , VK_IMAGE_TYPE_3D ,
VK_IMAGE_VIEW_TYPE_3D , gcm_format , 0 , 0 , width , height , depth , 1 , remap_vector , false ) ;
2021-07-26 20:12:05 +02:00
if ( ! result )
{
// Failed to create temporary object, bail
return nullptr ;
}
2021-07-14 00:25:32 +02:00
const auto image = result - > image ( ) ;
VkImageAspectFlags dst_aspect = vk : : get_aspect_flags ( result - > info . format ) ;
2025-04-05 21:50:45 +02:00
VkImageSubresourceRange dst_range = { dst_aspect , 0 , 1 , 0 , 1 } ;
2021-07-14 00:25:32 +02:00
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , dst_range ) ;
if ( ! ( dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT ) )
{
VkClearColorValue clear = { } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearColorImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2021-07-14 00:25:32 +02:00
}
else
{
2025-04-05 21:50:45 +02:00
VkClearDepthStencilValue clear = { 1.f , 0 } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearDepthStencilImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2021-07-14 00:25:32 +02:00
}
copy_transfer_regions_impl ( cmd , image , sections_to_copy ) ;
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , dst_range ) ;
return result ;
}
vk : : image_view * texture_cache : : generate_atlas_from_images ( vk : : command_buffer & cmd , u32 gcm_format , u16 width , u16 height ,
const std : : vector < copy_region_descriptor > & sections_to_copy , const rsx : : texture_channel_remap_t & remap_vector )
{
auto _template = get_template_from_collection_impl ( sections_to_copy ) ;
auto result = create_temporary_subresource_view_impl ( cmd , _template , VK_IMAGE_TYPE_2D ,
VK_IMAGE_VIEW_TYPE_2D , gcm_format , 0 , 0 , width , height , 1 , 1 , remap_vector , false ) ;
2021-07-26 20:12:05 +02:00
if ( ! result )
{
// Failed to create temporary object, bail
return nullptr ;
}
2021-07-14 00:25:32 +02:00
const auto image = result - > image ( ) ;
VkImageAspectFlags dst_aspect = vk : : get_aspect_flags ( result - > info . format ) ;
2025-04-05 21:50:45 +02:00
VkImageSubresourceRange dst_range = { dst_aspect , 0 , 1 , 0 , 1 } ;
2021-07-14 00:25:32 +02:00
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , dst_range ) ;
2023-06-20 19:59:32 +02:00
if ( sections_to_copy [ 0 ] . dst_w ! = width | | sections_to_copy [ 0 ] . dst_h ! = height )
2021-07-14 00:25:32 +02:00
{
2023-06-20 19:59:32 +02:00
if ( ! ( dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT ) )
{
VkClearColorValue clear = { } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearColorImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2023-06-20 19:59:32 +02:00
}
else
{
2025-04-05 21:50:45 +02:00
VkClearDepthStencilValue clear = { 1.f , 0 } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearDepthStencilImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2023-06-20 19:59:32 +02:00
}
2021-07-14 00:25:32 +02:00
}
copy_transfer_regions_impl ( cmd , image , sections_to_copy ) ;
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , dst_range ) ;
return result ;
}
vk : : image_view * texture_cache : : generate_2d_mipmaps_from_images ( vk : : command_buffer & cmd , u32 gcm_format , u16 width , u16 height ,
const std : : vector < copy_region_descriptor > & sections_to_copy , const rsx : : texture_channel_remap_t & remap_vector )
{
const auto mipmaps = : : narrow < u8 > ( sections_to_copy . size ( ) ) ;
auto _template = get_template_from_collection_impl ( sections_to_copy ) ;
auto result = create_temporary_subresource_view_impl ( cmd , _template , VK_IMAGE_TYPE_2D ,
VK_IMAGE_VIEW_TYPE_2D , gcm_format , 0 , 0 , width , height , 1 , mipmaps , remap_vector , false ) ;
2021-07-26 20:12:05 +02:00
if ( ! result )
{
// Failed to create temporary object, bail
return nullptr ;
}
2021-07-14 00:25:32 +02:00
const auto image = result - > image ( ) ;
VkImageAspectFlags dst_aspect = vk : : get_aspect_flags ( result - > info . format ) ;
2025-04-05 21:50:45 +02:00
VkImageSubresourceRange dst_range = { dst_aspect , 0 , mipmaps , 0 , 1 } ;
2021-07-14 00:25:32 +02:00
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , dst_range ) ;
if ( ! ( dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT ) )
{
VkClearColorValue clear = { } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearColorImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2021-07-14 00:25:32 +02:00
}
else
{
2025-04-05 21:50:45 +02:00
VkClearDepthStencilValue clear = { 1.f , 0 } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearDepthStencilImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & dst_range ) ;
2021-07-14 00:25:32 +02:00
}
copy_transfer_regions_impl ( cmd , image , sections_to_copy ) ;
vk : : change_image_layout ( cmd , image , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL , dst_range ) ;
return result ;
}
void texture_cache : : release_temporary_subresource ( vk : : image_view * view )
{
2022-02-05 19:11:56 +01:00
auto resource = dynamic_cast < vk : : viewable_image * > ( view - > image ( ) ) ;
ensure ( resource ) ;
auto image = std : : unique_ptr < vk : : viewable_image > ( resource ) ;
2022-02-08 22:44:17 +01:00
auto disposable = vk : : disposable_t : : make ( new cached_image_reference_t ( this , image ) ) ;
2022-02-05 19:11:56 +01:00
vk : : get_resource_manager ( ) - > dispose ( disposable ) ;
2021-07-14 00:25:32 +02:00
}
void texture_cache : : update_image_contents ( vk : : command_buffer & cmd , vk : : image_view * dst_view , vk : : image * src , u16 width , u16 height )
{
std : : vector < copy_region_descriptor > region =
2025-04-05 21:50:45 +02:00
{ { . src = src ,
. xform = rsx : : surface_transform : : identity ,
. src_w = width ,
. src_h = height ,
. dst_w = width ,
. dst_h = height } } ;
2021-07-14 00:25:32 +02:00
auto dst = dst_view - > image ( ) ;
dst - > push_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
copy_transfer_regions_impl ( cmd , dst , region ) ;
dst - > pop_layout ( cmd ) ;
}
2022-01-09 21:07:18 +01:00
cached_texture_section * texture_cache : : create_new_texture ( vk : : command_buffer & cmd , const utils : : address_range & rsx_range , u16 width , u16 height , u16 depth , u16 mipmaps , u32 pitch ,
2021-07-14 00:25:32 +02:00
u32 gcm_format , rsx : : texture_upload_context context , rsx : : texture_dimension_extended type , bool swizzled , rsx : : component_order swizzle_flags , rsx : : flags32_t flags )
{
const auto section_depth = depth ;
// Define desirable attributes based on type
VkImageType image_type ;
VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
u8 layer = 0 ;
switch ( type )
{
case rsx : : texture_dimension_extended : : texture_dimension_1d :
image_type = VK_IMAGE_TYPE_1D ;
height = 1 ;
depth = 1 ;
layer = 1 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_2d :
image_type = VK_IMAGE_TYPE_2D ;
depth = 1 ;
layer = 1 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_cubemap :
image_type = VK_IMAGE_TYPE_2D ;
depth = 1 ;
layer = 6 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_3d :
image_type = VK_IMAGE_TYPE_3D ;
layer = 1 ;
break ;
default :
fmt : : throw_exception ( " Unreachable " ) ;
}
// Check what actually exists at that address
2025-04-05 21:50:45 +02:00
const rsx : : image_section_attributes_t search_desc = { . gcm_format = gcm_format , . width = width , . height = height , . depth = section_depth , . mipmaps = mipmaps } ;
2021-07-14 00:25:32 +02:00
const bool allow_dirty = ( context ! = rsx : : texture_upload_context : : framebuffer_storage ) ;
cached_texture_section & region = * find_cached_texture ( rsx_range , search_desc , true , true , allow_dirty ) ;
ensure ( ! region . is_locked ( ) ) ;
vk : : viewable_image * image = nullptr ;
if ( region . exists ( ) )
{
image = dynamic_cast < vk : : viewable_image * > ( region . get_raw_texture ( ) ) ;
2022-07-24 19:28:57 +02:00
bool reusable = true ;
if ( flags & texture_create_flags : : do_not_reuse )
{
reusable = false ;
}
else if ( flags & texture_create_flags : : shareable )
{
reusable = ( image & & image - > sharing_mode ( ) = = VK_SHARING_MODE_CONCURRENT ) ;
}
if ( ! reusable | | ! image | | region . get_image_type ( ) ! = type | | image - > depth ( ) ! = depth ) // TODO
2021-07-14 00:25:32 +02:00
{
// Incompatible view/type
region . destroy ( ) ;
image = nullptr ;
}
else
{
ensure ( region . is_managed ( ) ) ;
// Reuse
region . set_rsx_pitch ( pitch ) ;
if ( flags & texture_create_flags : : initialize_image_contents )
{
// Wipe memory
image - > change_layout ( cmd , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
2025-04-05 21:50:45 +02:00
VkImageSubresourceRange range { image - > aspect ( ) , 0 , image - > mipmaps ( ) , 0 , image - > layers ( ) } ;
2021-07-14 00:25:32 +02:00
if ( image - > aspect ( ) & VK_IMAGE_ASPECT_COLOR_BIT )
{
2025-04-05 21:50:45 +02:00
VkClearColorValue color = { { 0.f , 0.f , 0.f , 1.f } } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearColorImage ) ( cmd , image - > value , image - > current_layout , & color , 1 , & range ) ;
2021-07-14 00:25:32 +02:00
}
else
{
2025-04-05 21:50:45 +02:00
VkClearDepthStencilValue clear { 1.f , 255 } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkCmdClearDepthStencilImage ) ( cmd , image - > value , image - > current_layout , & clear , 1 , & range ) ;
2021-07-14 00:25:32 +02:00
}
}
}
}
if ( ! image )
{
const bool is_cubemap = type = = rsx : : texture_dimension_extended : : texture_dimension_cubemap ;
const VkFormat vk_format = get_compatible_sampler_format ( m_formats_support , gcm_format ) ;
2022-07-24 19:28:57 +02:00
VkImageCreateFlags create_flags = is_cubemap ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0 ;
VkSharingMode sharing_mode = ( flags & texture_create_flags : : shareable ) ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE ;
2021-07-14 00:25:32 +02:00
2022-07-24 19:28:57 +02:00
if ( auto found = find_cached_image ( vk_format , width , height , depth , mipmaps , image_type , create_flags , usage_flags , sharing_mode ) )
2022-02-05 19:11:56 +01:00
{
image = found . release ( ) ;
}
else
{
2022-07-24 19:28:57 +02:00
if ( sharing_mode = = VK_SHARING_MODE_CONCURRENT )
{
create_flags | = VK_IMAGE_CREATE_SHAREABLE_RPCS3 ;
}
2022-02-05 19:11:56 +01:00
image = new vk : : viewable_image ( * m_device ,
m_memory_types . device_local , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
image_type , vk_format ,
width , height , depth , mipmaps , layer , VK_SAMPLE_COUNT_1_BIT , VK_IMAGE_LAYOUT_UNDEFINED ,
2022-02-08 20:49:09 +01:00
VK_IMAGE_TILING_OPTIMAL , usage_flags , create_flags ,
2022-02-05 19:11:56 +01:00
VMM_ALLOCATION_POOL_TEXTURE_CACHE , rsx : : classify_format ( gcm_format ) ) ;
}
2021-07-14 00:25:32 +02:00
// New section, we must prepare it
region . reset ( rsx_range ) ;
region . set_gcm_format ( gcm_format ) ;
region . set_image_type ( type ) ;
region . create ( width , height , section_depth , mipmaps , image , pitch , true , gcm_format ) ;
}
region . set_view_flags ( swizzle_flags ) ;
region . set_context ( context ) ;
region . set_swizzled ( swizzled ) ;
region . set_dirty ( false ) ;
image - > native_component_map = apply_component_mapping_flags ( gcm_format , swizzle_flags , rsx : : default_remap_vector ) ;
// Its not necessary to lock blit dst textures as they are just reused as necessary
switch ( context )
{
case rsx : : texture_upload_context : : shader_read :
case rsx : : texture_upload_context : : blit_engine_src :
region . protect ( utils : : protection : : ro ) ;
read_only_range = region . get_min_max ( read_only_range , rsx : : section_bounds : : locked_range ) ;
break ;
case rsx : : texture_upload_context : : blit_engine_dst :
region . set_unpack_swap_bytes ( true ) ;
no_access_range = region . get_min_max ( no_access_range , rsx : : section_bounds : : locked_range ) ;
break ;
case rsx : : texture_upload_context : : dma :
case rsx : : texture_upload_context : : framebuffer_storage :
2021-07-14 19:16:57 +02:00
// Should not be initialized with this method
2021-07-14 00:25:32 +02:00
default :
fmt : : throw_exception ( " Unexpected upload context 0x%x " , u32 ( context ) ) ;
}
update_cache_tag ( ) ;
return & region ;
}
2023-12-31 21:55:15 +01:00
cached_texture_section * texture_cache : : create_nul_section (
vk : : command_buffer & /*cmd*/ ,
const utils : : address_range & rsx_range ,
const rsx : : image_section_attributes_t & attrs ,
2024-02-05 23:29:51 +01:00
const rsx : : GCM_tile_reference & tile ,
2023-12-31 21:55:15 +01:00
bool memory_load )
2021-07-14 00:25:32 +02:00
{
2025-04-05 21:50:45 +02:00
auto & region = * find_cached_texture ( rsx_range , { . gcm_format = RSX_GCM_FORMAT_IGNORED } , true , false , false ) ;
2021-07-14 00:25:32 +02:00
ensure ( ! region . is_locked ( ) ) ;
// Prepare section
region . reset ( rsx_range ) ;
2023-12-31 21:55:15 +01:00
region . create_dma_only ( attrs . width , attrs . height , attrs . pitch ) ;
2021-07-14 00:25:32 +02:00
region . set_dirty ( false ) ;
region . set_unpack_swap_bytes ( true ) ;
2024-02-05 23:29:51 +01:00
if ( memory_load & & ! tile ) // Memory load on DMA tiles will always happen during the actual copy command
2021-07-14 00:25:32 +02:00
{
vk : : map_dma ( rsx_range . start , rsx_range . length ( ) ) ;
vk : : load_dma ( rsx_range . start , rsx_range . length ( ) ) ;
}
no_access_range = region . get_min_max ( no_access_range , rsx : : section_bounds : : locked_range ) ;
update_cache_tag ( ) ;
return & region ;
}
2022-01-09 21:07:18 +01:00
cached_texture_section * texture_cache : : upload_image_from_cpu ( vk : : command_buffer & cmd , const utils : : address_range & rsx_range , u16 width , u16 height , u16 depth , u16 mipmaps , u32 pitch , u32 gcm_format ,
2021-07-14 00:25:32 +02:00
rsx : : texture_upload_context context , const std : : vector < rsx : : subresource_layout > & subresource_layout , rsx : : texture_dimension_extended type , bool swizzled )
{
if ( context ! = rsx : : texture_upload_context : : shader_read )
{
if ( vk : : is_renderpass_open ( cmd ) )
{
vk : : end_renderpass ( cmd ) ;
}
}
2022-02-08 20:49:09 +01:00
2022-07-24 19:39:15 +02:00
const bool upload_async = rsx : : get_current_renderer ( ) - > get_backend_config ( ) . supports_asynchronous_compute ;
rsx : : flags32_t create_flags = 0 ;
if ( upload_async & & g_fxo - > get < AsyncTaskScheduler > ( ) . is_host_mode ( ) )
{
create_flags | = texture_create_flags : : do_not_reuse ;
if ( m_device - > get_graphics_queue ( ) ! = m_device - > get_transfer_queue ( ) )
{
create_flags | = texture_create_flags : : shareable ;
}
}
2021-07-14 00:25:32 +02:00
auto section = create_new_texture ( cmd , rsx_range , width , height , depth , mipmaps , pitch , gcm_format , context , type , swizzled ,
2022-01-30 12:56:22 +01:00
rsx : : component_order : : default_ , create_flags ) ;
2021-07-14 00:25:32 +02:00
auto image = section - > get_raw_texture ( ) ;
image - > set_debug_name ( fmt : : format ( " Raw Texture @0x%x " , rsx_range . start ) ) ;
vk : : enter_uninterruptible ( ) ;
bool input_swizzled = swizzled ;
if ( context = = rsx : : texture_upload_context : : blit_engine_src )
{
// Swizzling is ignored for blit engine copy and emulated using remapping
input_swizzled = false ;
}
2022-01-30 12:56:22 +01:00
rsx : : flags32_t upload_command_flags = initialize_image_layout | upload_contents_inline ;
2022-07-24 19:39:15 +02:00
if ( context = = rsx : : texture_upload_context : : shader_read & & upload_async )
2022-01-30 12:56:22 +01:00
{
upload_command_flags | = upload_contents_async ;
}
2021-07-14 00:25:32 +02:00
2023-10-02 22:49:44 +02:00
std : : vector < rsx : : subresource_layout > tmp ;
auto p_subresource_layout = & subresource_layout ;
u32 heap_align = upload_heap_align_default ;
if ( auto tiled_region = rsx : : get_current_renderer ( ) - > get_tiled_memory_region ( rsx_range ) ;
context = = rsx : : texture_upload_context : : blit_engine_src & & tiled_region )
{
if ( mipmaps > 1 )
{
// This really shouldn't happen on framebuffer tiled memory
rsx_log . error ( " Tiled decode of mipmapped textures is not supported. " ) ;
}
else
{
const auto bpp = rsx : : get_format_block_size_in_bytes ( gcm_format ) ;
const auto [ scratch_buf , linear_data_scratch_offset ] = vk : : detile_memory_block ( cmd , tiled_region , rsx_range , width , height , bpp ) ;
auto subres = subresource_layout . front ( ) ;
// FIXME: !!EVIL!!
2025-04-05 21:50:45 +02:00
subres . data = { scratch_buf , linear_data_scratch_offset } ;
2023-10-02 22:49:44 +02:00
subres . pitch_in_block = width ;
upload_command_flags | = source_is_gpu_resident ;
heap_align = width * bpp ;
tmp . push_back ( subres ) ;
p_subresource_layout = & tmp ;
}
}
2021-08-13 19:08:52 +02:00
const u16 layer_count = ( type = = rsx : : texture_dimension_extended : : texture_dimension_cubemap ) ? 6 : 1 ;
2023-10-02 22:49:44 +02:00
vk : : upload_image ( cmd , image , * p_subresource_layout , gcm_format , input_swizzled , layer_count , image - > aspect ( ) ,
* m_texture_upload_heap , heap_align , upload_command_flags ) ;
2021-07-14 00:25:32 +02:00
vk : : leave_uninterruptible ( ) ;
if ( context ! = rsx : : texture_upload_context : : shader_read )
{
// Insert appropriate barrier depending on use. Shader read resources should be lazy-initialized before consuming.
// TODO: All texture resources should be initialized on use, this is wasteful
VkImageLayout preferred_layout ;
switch ( context )
{
default :
preferred_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ;
break ;
case rsx : : texture_upload_context : : blit_engine_dst :
preferred_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ;
break ;
case rsx : : texture_upload_context : : blit_engine_src :
preferred_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ;
break ;
}
if ( preferred_layout ! = image - > current_layout )
{
image - > change_layout ( cmd , preferred_layout ) ;
}
else
{
// Insert ordering barrier
ensure ( preferred_layout = = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
insert_image_memory_barrier ( cmd , image - > value , image - > current_layout , preferred_layout ,
VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_ACCESS_TRANSFER_WRITE_BIT , VK_ACCESS_TRANSFER_WRITE_BIT ,
2025-04-05 21:50:45 +02:00
{ image - > aspect ( ) , 0 , image - > mipmaps ( ) , 0 , image - > layers ( ) } ) ;
2021-07-14 00:25:32 +02:00
}
}
section - > last_write_tag = rsx : : get_shared_tag ( ) ;
return section ;
}
void texture_cache : : set_component_order ( cached_texture_section & section , u32 gcm_format , rsx : : component_order expected_flags )
{
if ( expected_flags = = section . get_view_flags ( ) )
return ;
const VkComponentMapping mapping = apply_component_mapping_flags ( gcm_format , expected_flags , rsx : : default_remap_vector ) ;
auto image = static_cast < vk : : viewable_image * > ( section . get_raw_texture ( ) ) ;
ensure ( image ) ;
image - > set_native_component_layout ( mapping ) ;
section . set_view_flags ( expected_flags ) ;
}
void texture_cache : : insert_texture_barrier ( vk : : command_buffer & cmd , vk : : image * tex , bool strong_ordering )
{
if ( ! strong_ordering & & tex - > current_layout = = VK_IMAGE_LAYOUT_GENERAL )
{
// A previous barrier already exists, do nothing
return ;
}
vk : : as_rtt ( tex ) - > texture_barrier ( cmd ) ;
}
bool texture_cache : : render_target_format_is_compatible ( vk : : image * tex , u32 gcm_format )
{
auto vk_format = tex - > info . format ;
switch ( gcm_format )
{
default :
2025-04-05 21:50:45 +02:00
// TODO
2023-07-02 03:16:59 +02:00
err_once ( " Format incompatibility detected, reporting failure to force data copy (VK_FORMAT=0x%X, GCM_FORMAT=0x%X) " , static_cast < u32 > ( vk_format ) , gcm_format ) ;
2021-07-14 00:25:32 +02:00
return false ;
2021-12-24 13:46:37 +01:00
# ifndef __APPLE__
case CELL_GCM_TEXTURE_R5G6B5 :
return ( vk_format = = VK_FORMAT_R5G6B5_UNORM_PACK16 ) ;
# else
// R5G6B5 is not supported by Metal
case CELL_GCM_TEXTURE_R5G6B5 :
return ( vk_format = = VK_FORMAT_B8G8R8A8_UNORM ) ;
# endif
2021-07-14 00:25:32 +02:00
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT :
return ( vk_format = = VK_FORMAT_R16G16B16A16_SFLOAT ) ;
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT :
return ( vk_format = = VK_FORMAT_R32G32B32A32_SFLOAT ) ;
case CELL_GCM_TEXTURE_X32_FLOAT :
return ( vk_format = = VK_FORMAT_R32_SFLOAT ) ;
case CELL_GCM_TEXTURE_A8R8G8B8 :
case CELL_GCM_TEXTURE_D8R8G8B8 :
return ( vk_format = = VK_FORMAT_B8G8R8A8_UNORM | | vk_format = = VK_FORMAT_D24_UNORM_S8_UINT | | vk_format = = VK_FORMAT_D32_SFLOAT_S8_UINT ) ;
case CELL_GCM_TEXTURE_B8 :
return ( vk_format = = VK_FORMAT_R8_UNORM ) ;
case CELL_GCM_TEXTURE_G8B8 :
return ( vk_format = = VK_FORMAT_R8G8_UNORM ) ;
case CELL_GCM_TEXTURE_DEPTH24_D8 :
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT :
return ( vk_format = = VK_FORMAT_D24_UNORM_S8_UINT | | vk_format = = VK_FORMAT_D32_SFLOAT_S8_UINT ) ;
case CELL_GCM_TEXTURE_X16 :
case CELL_GCM_TEXTURE_DEPTH16 :
case CELL_GCM_TEXTURE_DEPTH16_FLOAT :
return ( vk_format = = VK_FORMAT_D16_UNORM | | vk_format = = VK_FORMAT_D32_SFLOAT ) ;
}
}
void texture_cache : : prepare_for_dma_transfers ( vk : : command_buffer & cmd )
{
if ( ! cmd . is_recording ( ) )
{
cmd . begin ( ) ;
}
}
void texture_cache : : cleanup_after_dma_transfers ( vk : : command_buffer & cmd )
{
bool occlusion_query_active = ! ! ( cmd . flags & vk : : command_buffer : : cb_has_open_query ) ;
if ( occlusion_query_active )
{
// We really stepped in it
vk : : do_query_cleanup ( cmd ) ;
}
// End recording
cmd . end ( ) ;
if ( cmd . access_hint ! = vk : : command_buffer : : access_type_hint : : all )
{
// Flush any pending async jobs in case of blockers
// TODO: Context-level manager should handle this logic
2022-07-24 19:39:15 +02:00
auto async_scheduler = g_fxo - > try_get < AsyncTaskScheduler > ( ) ;
2022-01-30 12:56:22 +01:00
vk : : semaphore * async_sema = nullptr ;
2022-07-24 19:39:15 +02:00
if ( async_scheduler & & async_scheduler - > is_recording ( ) )
2022-01-30 12:56:22 +01:00
{
2022-07-24 19:39:15 +02:00
if ( async_scheduler - > is_host_mode ( ) )
2022-01-30 12:56:22 +01:00
{
2022-07-24 19:39:15 +02:00
async_sema = async_scheduler - > get_sema ( ) ;
2022-01-30 12:56:22 +01:00
}
else
{
vk : : queue_submit_t submit_info { } ;
2022-07-24 19:39:15 +02:00
async_scheduler - > flush ( submit_info , VK_TRUE ) ;
2022-01-30 12:56:22 +01:00
}
}
2021-07-14 00:25:32 +02:00
// Primary access command queue, must restart it after
vk : : fence submit_fence ( * m_device ) ;
2025-04-05 21:50:45 +02:00
vk : : queue_submit_t submit_info { m_submit_queue , & submit_fence } ;
2022-01-30 12:56:22 +01:00
if ( async_sema )
{
vk : : queue_submit_t submit_info2 { } ;
2022-07-24 19:39:15 +02:00
submit_info2 . queue_signal ( * async_sema ) ;
2022-07-24 19:51:29 +02:00
async_scheduler - > flush ( submit_info2 , VK_TRUE ) ;
2022-07-24 19:39:15 +02:00
submit_info . wait_on ( * async_sema , VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ) ;
2022-01-30 12:56:22 +01:00
}
2021-07-14 00:25:32 +02:00
2022-07-24 19:39:15 +02:00
cmd . submit ( submit_info , VK_TRUE ) ;
2021-07-14 00:25:32 +02:00
vk : : wait_for_fence ( & submit_fence , GENERAL_WAIT_TIMEOUT ) ;
2025-03-18 16:19:05 +01:00
CHECK_RESULT ( VK_GET_SYMBOL ( vkResetCommandBuffer ) ( cmd , 0 ) ) ;
2021-07-14 00:25:32 +02:00
cmd . begin ( ) ;
}
else
{
// Auxilliary command queue with auto-restart capability
2025-04-05 21:50:45 +02:00
vk : : queue_submit_t submit_info { m_submit_queue , nullptr } ;
2022-01-30 12:56:22 +01:00
cmd . submit ( submit_info , VK_TRUE ) ;
2021-07-14 00:25:32 +02:00
}
ensure ( cmd . flags = = 0 ) ;
if ( occlusion_query_active )
{
ensure ( cmd . is_recording ( ) ) ;
cmd . flags | = vk : : command_buffer : : cb_load_occluson_task ;
}
}
void texture_cache : : initialize ( vk : : render_device & device , VkQueue submit_queue , vk : : data_heap & upload_heap )
{
m_device = & device ;
m_memory_types = device . get_memory_mapping ( ) ;
m_formats_support = device . get_formats_support ( ) ;
m_submit_queue = submit_queue ;
m_texture_upload_heap = & upload_heap ;
}
void texture_cache : : destroy ( )
{
clear ( ) ;
}
bool texture_cache : : is_depth_texture ( u32 rsx_address , u32 rsx_size )
{
reader_lock lock ( m_cache_mutex ) ;
auto & block = m_storage . block_for ( rsx_address ) ;
if ( block . get_locked_count ( ) = = 0 )
return false ;
for ( auto & tex : block )
{
if ( tex . is_dirty ( ) )
continue ;
if ( ! tex . overlaps ( rsx_address , rsx : : section_bounds : : full_range ) )
continue ;
if ( ( rsx_address + rsx_size - tex . get_section_base ( ) ) < = tex . get_section_size ( ) )
{
switch ( tex . get_format ( ) )
{
case VK_FORMAT_D16_UNORM :
case VK_FORMAT_D32_SFLOAT :
case VK_FORMAT_D32_SFLOAT_S8_UINT :
case VK_FORMAT_D24_UNORM_S8_UINT :
return true ;
default :
return false ;
}
}
}
2025-04-05 21:50:45 +02:00
// Unreachable; silence compiler warning anyway
2021-07-14 00:25:32 +02:00
return false ;
}
2021-07-14 19:09:16 +02:00
bool texture_cache : : handle_memory_pressure ( rsx : : problem_severity severity )
{
2021-07-23 19:15:07 +02:00
auto any_released = baseclass : : handle_memory_pressure ( severity ) ;
2021-07-14 19:09:16 +02:00
2021-07-23 19:15:07 +02:00
// TODO: This can cause invalidation of in-flight resources
2022-02-05 19:11:56 +01:00
if ( severity < = rsx : : problem_severity : : low | | ! m_cached_memory_size )
2021-07-14 19:09:16 +02:00
{
// Nothing left to do
return any_released ;
}
constexpr u64 _1M = 0x100000 ;
2022-02-05 19:11:56 +01:00
if ( severity < = rsx : : problem_severity : : moderate & & m_cached_memory_size < ( 64 * _1M ) )
2021-07-14 19:09:16 +02:00
{
// Some memory is consumed by the temporary resources, but no need to panic just yet
return any_released ;
}
2021-07-23 19:15:07 +02:00
std : : unique_lock lock ( m_cache_mutex , std : : defer_lock ) ;
if ( ! lock . try_lock ( ) )
{
rsx_log . warning ( " Unable to remove temporary resources because we're already in the texture cache! " ) ;
return any_released ;
}
2021-07-14 19:09:16 +02:00
// Nuke temporary resources. They will still be visible to the GPU.
2023-05-14 21:50:22 +02:00
auto gc = vk : : get_resource_manager ( ) ;
2022-02-21 19:33:20 +01:00
any_released | = ! m_cached_images . empty ( ) ;
2023-05-14 21:50:22 +02:00
for ( auto & img : m_cached_images )
{
gc - > dispose ( img . data ) ;
}
2022-02-05 19:11:56 +01:00
m_cached_images . clear ( ) ;
m_cached_memory_size = 0 ;
2021-07-14 19:09:16 +02:00
2022-02-21 19:33:20 +01:00
any_released | = ! m_temporary_subresource_cache . empty ( ) ;
2023-05-14 21:50:22 +02:00
for ( auto & e : m_temporary_subresource_cache )
{
ensure ( e . second . second ) ;
release_temporary_subresource ( e . second . second ) ;
}
2021-07-14 19:09:16 +02:00
m_temporary_subresource_cache . clear ( ) ;
2022-02-21 19:33:20 +01:00
return any_released ;
2021-07-14 19:09:16 +02:00
}
2021-07-14 00:25:32 +02:00
void texture_cache : : on_frame_end ( )
{
trim_sections ( ) ;
2022-02-05 19:11:56 +01:00
if ( m_storage . m_unreleased_texture_objects > = m_max_zombie_objects )
2021-07-14 00:25:32 +02:00
{
purge_unreleased_sections ( ) ;
}
2022-02-05 19:11:56 +01:00
if ( m_cached_images . size ( ) > max_cached_image_pool_size | |
m_cached_memory_size > 256 * 0x100000 )
2021-07-14 00:25:32 +02:00
{
2022-02-05 19:11:56 +01:00
std : : lock_guard lock ( m_cached_pool_lock ) ;
const auto new_size = m_cached_images . size ( ) / 2 ;
for ( usz i = new_size ; i < m_cached_images . size ( ) ; + + i )
2021-07-14 00:25:32 +02:00
{
2022-02-06 12:23:46 +01:00
m_cached_memory_size - = m_cached_images [ i ] . data - > memory - > size ( ) ;
2021-07-14 00:25:32 +02:00
}
2022-02-05 19:11:56 +01:00
m_cached_images . resize ( new_size ) ;
}
2021-07-14 00:25:32 +02:00
baseclass : : on_frame_end ( ) ;
2022-02-05 19:11:56 +01:00
reset_frame_statistics ( ) ;
2021-07-14 00:25:32 +02:00
}
2021-08-04 02:04:48 +02:00
vk : : viewable_image * texture_cache : : upload_image_simple ( vk : : command_buffer & cmd , VkFormat format , u32 address , u32 width , u32 height , u32 pitch )
2021-07-14 00:25:32 +02:00
{
bool linear_format_supported = false ;
switch ( format )
{
case VK_FORMAT_B8G8R8A8_UNORM :
linear_format_supported = m_formats_support . bgra8_linear ;
break ;
case VK_FORMAT_R8G8B8A8_UNORM :
linear_format_supported = m_formats_support . argb8_linear ;
break ;
default :
rsx_log . error ( " Unsupported VkFormat 0x%x " , static_cast < u32 > ( format ) ) ;
return nullptr ;
}
if ( ! linear_format_supported )
{
return nullptr ;
}
// Uploads a linear memory range as a BGRA8 texture
auto image = std : : make_unique < vk : : viewable_image > ( * m_device , m_memory_types . host_visible_coherent ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ,
VK_IMAGE_TYPE_2D ,
format ,
width , height , 1 , 1 , 1 , VK_SAMPLE_COUNT_1_BIT , VK_IMAGE_LAYOUT_PREINITIALIZED ,
VK_IMAGE_TILING_LINEAR , VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT , 0 ,
2022-02-08 20:49:09 +01:00
VMM_ALLOCATION_POOL_SWAPCHAIN ) ;
2021-07-14 00:25:32 +02:00
VkImageSubresource subresource { } ;
subresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
VkSubresourceLayout layout { } ;
2025-03-18 16:19:05 +01:00
VK_GET_SYMBOL ( vkGetImageSubresourceLayout ) ( * m_device , image - > value , & subresource , & layout ) ;
2021-07-14 00:25:32 +02:00
void * mem = image - > memory - > map ( 0 , layout . rowPitch * height ) ;
2023-09-14 20:12:19 +02:00
auto src = vm : : _ptr < const char > ( address ) ;
2021-07-14 00:25:32 +02:00
auto dst = static_cast < char * > ( mem ) ;
2021-07-14 19:16:57 +02:00
// TODO: SSE optimization
2021-07-14 00:25:32 +02:00
for ( u32 row = 0 ; row < height ; + + row )
{
auto casted_src = reinterpret_cast < const be_t < u32 > * > ( src ) ;
auto casted_dst = reinterpret_cast < u32 * > ( dst ) ;
for ( u32 col = 0 ; col < width ; + + col )
casted_dst [ col ] = casted_src [ col ] ;
src + = pitch ;
dst + = layout . rowPitch ;
}
image - > memory - > unmap ( ) ;
vk : : change_image_layout ( cmd , image . get ( ) , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) ;
2024-01-04 01:24:23 +01:00
// Fully dispose immediately. These immages aren't really reusable right now.
2021-07-14 00:25:32 +02:00
auto result = image . get ( ) ;
2024-01-04 01:24:23 +01:00
vk : : get_resource_manager ( ) - > dispose ( image ) ;
2021-07-14 00:25:32 +02:00
return result ;
}
2023-12-24 18:09:36 +01:00
bool texture_cache : : blit ( const rsx : : blit_src_info & src , const rsx : : blit_dst_info & dst , bool interpolate , vk : : surface_cache & m_rtts , vk : : command_buffer & cmd )
2021-07-14 00:25:32 +02:00
{
blitter helper ;
auto reply = upload_scaled_image ( src , dst , interpolate , cmd , m_rtts , helper ) ;
if ( reply . succeeded )
{
if ( reply . real_dst_size )
{
flush_if_cache_miss_likely ( cmd , reply . to_address_range ( ) ) ;
}
return true ;
}
return false ;
}
u32 texture_cache : : get_unreleased_textures_count ( ) const
{
2022-02-05 19:11:56 +01:00
return baseclass : : get_unreleased_textures_count ( ) + : : size32 ( m_cached_images ) ;
2021-07-14 00:25:32 +02:00
}
2022-02-05 19:53:34 +01:00
u64 texture_cache : : get_temporary_memory_in_use ( ) const
2021-07-14 00:25:32 +02:00
{
2022-02-05 19:53:34 +01:00
// TODO: Technically incorrect, we should have separate metrics for cached evictable resources (this value) and temporary active resources.
2022-02-05 19:11:56 +01:00
return m_cached_memory_size ;
2021-07-14 00:25:32 +02:00
}
2021-07-14 00:26:20 +02:00
bool texture_cache : : is_overallocated ( ) const
{
const auto total_device_memory = m_device - > get_memory_mapping ( ) . device_local_total_bytes / 0x100000 ;
u64 quota = 0 ;
2023-05-13 01:28:17 +02:00
if ( total_device_memory > = 2048 )
2021-07-14 00:26:20 +02:00
{
2021-07-14 19:18:53 +02:00
quota = std : : min < u64 > ( 3072 , ( total_device_memory * 40 ) / 100 ) ;
2021-07-14 00:26:20 +02:00
}
2023-05-13 01:28:17 +02:00
else if ( total_device_memory > = 1024 )
{
quota = std : : max < u64 > ( 204 , ( total_device_memory * 30 ) / 100 ) ;
}
2021-07-14 00:26:20 +02:00
else if ( total_device_memory > = 768 )
{
2023-05-13 01:28:17 +02:00
quota = 192 ;
2021-07-14 00:26:20 +02:00
}
else
{
2021-07-14 19:18:53 +02:00
quota = std : : min < u64 > ( 128 , total_device_memory / 2 ) ;
2021-07-14 00:26:20 +02:00
}
quota * = 0x100000 ;
if ( const u64 texture_cache_pool_usage = vmm_get_application_pool_usage ( VMM_ALLOCATION_POOL_TEXTURE_CACHE ) ;
texture_cache_pool_usage > quota )
{
rsx_log . warning ( " Texture cache is using %lluM of memory which exceeds the allocation quota of %lluM " ,
texture_cache_pool_usage , quota ) ;
return true ;
}
return false ;
}
2025-04-05 21:50:45 +02:00
} // namespace vk