2020-12-05 13:08:24 +01:00
# pragma once
2017-09-08 16:52:13 +02:00
2020-12-13 12:54:43 +01:00
# include "texture_cache_utils.h"
2018-10-28 14:59:39 +01:00
# include "texture_cache_predictor.h"
2019-10-08 17:07:36 +02:00
# include "texture_cache_helpers.h"
2017-09-08 16:52:13 +02:00
2020-12-22 16:04:08 +01:00
# include <unordered_map>
2021-03-23 20:32:50 +01:00
# include "Emu/Cell/timers.hpp"
2017-10-28 21:17:27 +02:00
2019-09-09 18:14:57 +02:00
# define RSX_GCM_FORMAT_IGNORED 0
2017-09-08 16:52:13 +02:00
namespace rsx
{
2019-11-17 21:03:18 +01:00
namespace helpers = rsx : : texture_cache_helpers ;
2018-10-28 14:59:39 +01:00
template < typename derived_type , typename _traits >
class texture_cache
2017-09-18 19:22:34 +02:00
{
2018-09-22 02:14:26 +02:00
public :
2018-10-28 14:59:39 +01:00
using traits = _traits ;
using commandbuffer_type = typename traits : : commandbuffer_type ;
using section_storage_type = typename traits : : section_storage_type ;
using image_resource_type = typename traits : : image_resource_type ;
using image_view_type = typename traits : : image_view_type ;
using image_storage_type = typename traits : : image_storage_type ;
using texture_format = typename traits : : texture_format ;
using predictor_type = texture_cache_predictor < traits > ;
using ranged_storage = rsx : : ranged_storage < traits > ;
2018-09-22 02:14:26 +02:00
using ranged_storage_block = typename ranged_storage : : block_type ;
2017-10-21 23:12:32 +02:00
2019-10-08 17:07:36 +02:00
using copy_region_descriptor = copy_region_descriptor_base < typename traits : : image_resource_type > ;
2018-09-22 02:14:26 +02:00
private :
2018-10-28 14:59:39 +01:00
static_assert ( std : : is_base_of < rsx : : cached_texture_section < section_storage_type , traits > , section_storage_type > : : value , " section_storage_type must derive from rsx::cached_texture_section " ) ;
2018-09-22 02:14:26 +02:00
/**
* Helper structs / enums
*/
2017-09-18 19:22:34 +02:00
2018-09-22 02:14:26 +02:00
// Keep track of cache misses to pre-emptively flush some addresses
struct framebuffer_memory_characteristics
2018-09-19 00:21:57 +02:00
{
2018-09-22 02:14:26 +02:00
u32 misses ;
texture_format format ;
} ;
2018-09-19 00:21:57 +02:00
2018-09-22 02:14:26 +02:00
public :
//Struct to hold data on sections to be paged back onto cpu memory
struct thrashed_set
2017-09-18 19:22:34 +02:00
{
2018-09-22 02:14:26 +02:00
bool violation_handled = false ;
bool flushed = false ;
2021-01-28 19:53:49 +01:00
bool invalidate_samplers = false ;
2018-09-22 02:14:26 +02:00
invalidation_cause cause ;
std : : vector < section_storage_type * > sections_to_flush ; // Sections to be flushed
std : : vector < section_storage_type * > sections_to_unprotect ; // These sections are to be unpotected and discarded by caller
std : : vector < section_storage_type * > sections_to_exclude ; // These sections are do be excluded from protection manipulation (subtracted from other sections)
u32 num_flushable = 0 ;
u64 cache_tag = 0 ;
2018-11-01 02:31:12 +01:00
2018-09-22 02:14:26 +02:00
address_range fault_range ;
address_range invalidate_range ;
2017-09-18 19:22:34 +02:00
2018-09-22 02:14:26 +02:00
void clear_sections ( )
2017-09-18 19:22:34 +02:00
{
2018-09-22 02:14:26 +02:00
sections_to_flush = { } ;
sections_to_unprotect = { } ;
sections_to_exclude = { } ;
num_flushable = 0 ;
2017-09-18 19:22:34 +02:00
}
2018-09-22 02:14:26 +02:00
bool empty ( ) const
2018-06-12 18:22:02 +02:00
{
2018-09-22 02:14:26 +02:00
return sections_to_flush . empty ( ) & & sections_to_unprotect . empty ( ) & & sections_to_exclude . empty ( ) ;
2018-06-12 18:22:02 +02:00
}
2018-09-22 02:14:26 +02:00
bool is_flushed ( ) const
2018-06-24 12:23:30 +02:00
{
2018-09-22 02:14:26 +02:00
return flushed | | sections_to_flush . empty ( ) ;
2018-06-24 12:23:30 +02:00
}
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
void check_pre_sanity ( ) const
2018-06-12 18:22:02 +02:00
{
2020-12-18 08:39:54 +01:00
usz flush_and_unprotect_count = sections_to_flush . size ( ) + sections_to_unprotect . size ( ) ;
usz exclude_count = sections_to_exclude . size ( ) ;
2018-06-12 18:22:02 +02:00
2018-09-22 02:14:26 +02:00
//-------------------------
// It is illegal to have only exclusions except when reading from a range with only RO sections
2020-12-09 16:04:52 +01:00
ensure ( flush_and_unprotect_count > 0 | | exclude_count = = 0 | | cause . is_read ( ) ) ;
2018-09-22 02:14:26 +02:00
if ( flush_and_unprotect_count = = 0 & & exclude_count > 0 )
2018-06-12 18:22:02 +02:00
{
2018-09-22 02:14:26 +02:00
// double-check that only RO sections exists
for ( auto * tex : sections_to_exclude )
2020-12-09 16:04:52 +01:00
ensure ( tex - > get_protection ( ) = = utils : : protection : : ro ) ;
2018-06-12 18:22:02 +02:00
}
2018-09-22 02:14:26 +02:00
//-------------------------
// Check that the number of sections we "found" matches the sections known to be in the fault range
const auto min_overlap_fault_no_ro = tex_cache_checker . get_minimum_number_of_sections ( fault_range ) ;
const auto min_overlap_invalidate_no_ro = tex_cache_checker . get_minimum_number_of_sections ( invalidate_range ) ;
2018-06-23 16:50:34 +02:00
2018-10-19 00:22:00 +02:00
const u16 min_overlap_fault = min_overlap_fault_no_ro . first + ( cause . is_read ( ) ? 0 : min_overlap_fault_no_ro . second ) ;
const u16 min_overlap_invalidate = min_overlap_invalidate_no_ro . first + ( cause . is_read ( ) ? 0 : min_overlap_invalidate_no_ro . second ) ;
2018-09-22 02:14:26 +02:00
AUDIT ( min_overlap_fault < = min_overlap_invalidate ) ;
2018-06-23 16:50:34 +02:00
2018-09-22 02:14:26 +02:00
const u16 min_flush_or_unprotect = min_overlap_fault ;
2017-12-04 17:30:09 +01:00
2018-09-22 02:14:26 +02:00
// we must flush or unprotect *all* sections that partially overlap the fault range
2020-12-09 16:04:52 +01:00
ensure ( flush_and_unprotect_count > = min_flush_or_unprotect ) ;
2017-12-04 17:30:09 +01:00
2018-09-22 02:14:26 +02:00
// result must contain *all* sections that overlap (completely or partially) the invalidation range
2020-12-09 16:04:52 +01:00
ensure ( flush_and_unprotect_count + exclude_count > = min_overlap_invalidate ) ;
2017-12-04 17:30:09 +01:00
}
2018-09-22 02:14:26 +02:00
void check_post_sanity ( ) const
2018-02-03 09:37:42 +01:00
{
2018-09-22 02:14:26 +02:00
AUDIT ( is_flushed ( ) ) ;
2018-02-03 09:37:42 +01:00
2018-09-22 02:14:26 +02:00
// Check that the number of sections we "found" matches the sections known to be in the fault range
tex_cache_checker . check_unprotected ( fault_range , cause . is_read ( ) & & invalidation_keep_ro_during_read , true ) ;
2017-12-04 17:30:09 +01:00
2018-09-22 02:14:26 +02:00
// Check that the cache has the correct protections
tex_cache_checker . verify ( ) ;
2018-02-10 09:52:44 +01:00
}
2018-09-22 02:14:26 +02:00
# endif // TEXTURE_CACHE_DEBUG
2017-12-04 17:30:09 +01:00
} ;
2018-09-22 02:14:26 +02:00
struct intersecting_set
2017-12-04 17:30:09 +01:00
{
2018-09-22 02:14:26 +02:00
std : : vector < section_storage_type * > sections = { } ;
address_range invalidate_range = { } ;
bool has_flushables = false ;
2017-11-03 12:16:55 +01:00
} ;
2019-10-08 17:07:36 +02:00
struct deferred_subresource : image_section_attributes_t
2017-11-03 12:16:55 +01:00
{
image_resource_type external_handle = 0 ;
2018-02-21 11:46:23 +01:00
std : : vector < copy_region_descriptor > sections_to_copy ;
2018-03-18 12:40:26 +01:00
texture_channel_remap_t remap ;
2019-02-25 16:03:14 +01:00
deferred_request_command op = deferred_request_command : : nop ;
2017-11-03 12:16:55 +01:00
u16 x = 0 ;
u16 y = 0 ;
2019-10-10 19:00:45 +02:00
utils : : address_range cache_range ;
2019-04-01 17:45:19 +02:00
bool do_not_cache = false ;
2017-11-03 12:16:55 +01:00
2019-06-08 09:33:48 +02:00
deferred_subresource ( ) = default ;
2017-11-03 12:16:55 +01:00
2019-10-08 17:07:36 +02:00
deferred_subresource ( image_resource_type _res , deferred_request_command _op ,
const image_section_attributes_t & attr , position2u offset ,
texture_channel_remap_t _remap )
2020-02-21 13:20:10 +01:00
: external_handle ( _res )
, remap ( std : : move ( _remap ) )
, op ( _op )
, x ( offset . x )
, y ( offset . y )
2018-09-22 02:14:26 +02:00
{
2019-10-08 17:07:36 +02:00
static_cast < image_section_attributes_t & > ( * this ) = attr ;
2018-09-22 02:14:26 +02:00
}
2018-02-10 17:21:16 +01:00
} ;
2017-11-03 12:16:55 +01:00
struct sampled_image_descriptor : public sampled_image_descriptor_base
{
image_view_type image_handle = 0 ;
deferred_subresource external_subresource_desc = { } ;
bool flag = false ;
2019-06-08 09:33:48 +02:00
sampled_image_descriptor ( ) = default ;
2017-09-08 16:52:13 +02:00
2020-08-15 23:33:34 +02:00
sampled_image_descriptor ( image_view_type handle , texture_upload_context ctx , rsx : : format_class ftype ,
2022-03-13 09:32:04 +01:00
size3f scale , rsx : : texture_dimension_extended type , bool cyclic_reference = false ,
u8 msaa_samples = 1 )
2017-11-03 12:16:55 +01:00
{
image_handle = handle ;
upload_context = ctx ;
2019-08-14 00:38:31 +02:00
format_class = ftype ;
2019-05-17 14:46:46 +02:00
is_cyclic_reference = cyclic_reference ;
2019-10-08 17:07:36 +02:00
scale_x = scale . width ;
scale_y = scale . height ;
2021-07-31 16:27:16 +02:00
scale_z = scale . depth ;
2017-11-03 12:16:55 +01:00
image_type = type ;
2022-03-13 09:32:04 +01:00
samples = msaa_samples ;
2017-11-03 12:16:55 +01:00
}
2019-10-08 17:07:36 +02:00
sampled_image_descriptor ( image_resource_type external_handle , deferred_request_command reason ,
const image_section_attributes_t & attr , position2u src_offset ,
2021-07-31 16:27:16 +02:00
texture_upload_context ctx , rsx : : format_class ftype , size3f scale ,
2018-03-30 12:28:46 +02:00
rsx : : texture_dimension_extended type , const texture_channel_remap_t & remap )
2017-11-03 12:16:55 +01:00
{
2019-10-08 17:07:36 +02:00
external_subresource_desc = { external_handle , reason , attr , src_offset , remap } ;
2017-11-03 12:16:55 +01:00
image_handle = 0 ;
upload_context = ctx ;
2019-08-14 00:38:31 +02:00
format_class = ftype ;
2019-10-08 17:07:36 +02:00
scale_x = scale . width ;
scale_y = scale . height ;
2021-07-31 16:27:16 +02:00
scale_z = scale . depth ;
2017-11-03 12:16:55 +01:00
image_type = type ;
}
2018-08-22 13:18:45 +02:00
2019-02-25 16:03:14 +01:00
void simplify ( )
{
// Optimizations in the straightforward methods copy_image_static and copy_image_dynamic make them preferred over the atlas method
if ( external_subresource_desc . op = = deferred_request_command : : atlas_gather & &
external_subresource_desc . sections_to_copy . size ( ) = = 1 )
{
// Check if the subresource fills the target, if so, change the command to copy_image_static
const auto & cpy = external_subresource_desc . sections_to_copy . front ( ) ;
if ( cpy . dst_x = = 0 & & cpy . dst_y = = 0 & &
cpy . dst_w = = external_subresource_desc . width & & cpy . dst_h = = external_subresource_desc . height & &
cpy . src_w = = cpy . dst_w & & cpy . src_h = = cpy . dst_h )
{
external_subresource_desc . external_handle = cpy . src ;
external_subresource_desc . x = cpy . src_x ;
external_subresource_desc . y = cpy . src_y ;
external_subresource_desc . op = deferred_request_command : : copy_image_static ;
}
}
}
2020-01-23 21:20:22 +01:00
// Returns true if at least threshold% is covered in pixels
2020-02-03 18:31:44 +01:00
bool atlas_covers_target_area ( int threshold ) const
2019-03-04 19:22:45 +01:00
{
2022-01-22 12:00:45 +01:00
const int target_area = ( external_subresource_desc . width * external_subresource_desc . height * external_subresource_desc . depth * threshold ) / 100 ;
2020-02-03 18:31:44 +01:00
int covered_area = 0 ;
2021-05-22 09:35:15 +02:00
areai bbox { smax , smax , 0 , 0 } ;
2019-03-04 19:22:45 +01:00
2020-02-03 18:31:44 +01:00
for ( const auto & section : external_subresource_desc . sections_to_copy )
2019-03-04 19:22:45 +01:00
{
2022-01-22 12:56:45 +01:00
if ( section . level ! = 0 )
{
// Ignore other slices other than mip0
continue ;
}
// Calculate virtual Y coordinate
const auto dst_y = ( section . dst_z * external_subresource_desc . height ) + section . dst_y ;
// Add this slice's dimensions to the total
2020-02-03 18:31:44 +01:00
covered_area + = section . dst_w * section . dst_h ;
2019-03-04 19:22:45 +01:00
2022-01-22 12:56:45 +01:00
// Extend the covered bbox
2020-02-03 18:31:44 +01:00
bbox . x1 = std : : min < int > ( section . dst_x , bbox . x1 ) ;
bbox . x2 = std : : max < int > ( section . dst_x + section . dst_w , bbox . x2 ) ;
2022-01-22 12:56:45 +01:00
bbox . y1 = std : : min < int > ( dst_y , bbox . y1 ) ;
bbox . y2 = std : : max < int > ( dst_y + section . dst_h , bbox . y2 ) ;
2020-02-03 18:31:44 +01:00
}
2019-03-04 19:22:45 +01:00
2020-02-03 18:31:44 +01:00
if ( covered_area < target_area )
{
return false ;
}
if ( const auto bounds_area = bbox . width ( ) * bbox . height ( ) ;
bounds_area < target_area )
{
return false ;
2019-03-04 19:22:45 +01:00
}
2020-02-03 18:31:44 +01:00
return true ;
2019-03-04 19:22:45 +01:00
}
2018-08-22 13:18:45 +02:00
u32 encoded_component_map ( ) const override
{
if ( image_handle )
{
return image_handle - > encoded_component_map ( ) ;
}
return 0 ;
}
2019-02-25 16:03:14 +01:00
bool validate ( ) const
{
return ( image_handle | | external_subresource_desc . op ! = deferred_request_command : : nop ) ;
}
2020-12-14 18:24:28 +01:00
/**
* Returns a boolean true / false if the descriptor is expired
* Optionally returns a second variable that contains the surface reference .
* The surface reference can be used to insert a texture barrier or inject a deferred resource
*/
2020-12-15 18:40:54 +01:00
template < typename surface_store_type , typename surface_type = typename surface_store_type : : surface_type >
std : : pair < bool , surface_type > is_expired ( surface_store_type & surface_cache )
2020-12-14 18:24:28 +01:00
{
2022-03-05 14:19:17 +01:00
if ( upload_context ! = rsx : : texture_upload_context : : framebuffer_storage )
2020-12-14 18:24:28 +01:00
{
2022-03-05 14:19:17 +01:00
return { } ;
2020-12-14 18:24:28 +01:00
}
// Expired, but may still be valid. Check if the texture is still accessible
auto ref_image = image_handle ? image_handle - > image ( ) : external_subresource_desc . external_handle ;
2022-03-05 14:19:17 +01:00
surface_type surface = dynamic_cast < surface_type > ( ref_image ) ;
// Try and grab a cache reference in case of MSAA resolve target or compositing op
if ( ! surface )
2020-12-14 18:24:28 +01:00
{
2022-03-05 14:19:17 +01:00
if ( ! ( surface = surface_cache . get_surface_at ( ref_address ) ) )
{
// Compositing op. Just ignore expiry for now
ensure ( ! ref_image ) ;
return { } ;
}
}
ensure ( surface ) ;
if ( ! ref_image | | surface - > get_surface ( rsx : : surface_access : : gpu_reference ) = = ref_image )
{
// Same image, so configuration did not change.
2022-03-06 10:45:46 +01:00
if ( surface_cache . cache_tag < = surface_cache_tag & &
surface - > last_use_tag < = surface_cache_tag )
2022-03-05 14:19:17 +01:00
{
external_subresource_desc . do_not_cache = false ;
return { } ;
}
// Image was written to since last bind. Insert texture barrier.
surface_cache_tag = surface - > last_use_tag ;
is_cyclic_reference = surface_cache . address_is_bound ( ref_address ) ;
external_subresource_desc . do_not_cache = is_cyclic_reference ;
switch ( external_subresource_desc . op )
2020-12-14 18:24:28 +01:00
{
2022-03-05 14:19:17 +01:00
case deferred_request_command : : copy_image_dynamic :
case deferred_request_command : : copy_image_static :
external_subresource_desc . op = ( is_cyclic_reference ) ? deferred_request_command : : copy_image_dynamic : deferred_request_command : : copy_image_static ;
[ [ fallthrough ] ] ;
default :
2020-12-15 18:40:54 +01:00
return { false , surface } ;
2020-12-14 18:24:28 +01:00
}
}
2022-03-05 14:19:17 +01:00
// Reupload
2020-12-14 18:24:28 +01:00
return { true , nullptr } ;
}
2017-11-03 12:16:55 +01:00
} ;
2018-09-22 02:14:26 +02:00
2017-11-03 12:16:55 +01:00
protected :
2017-12-07 13:08:11 +01:00
2018-09-22 02:14:26 +02:00
/**
* Variable declarations
*/
2017-09-08 16:52:13 +02:00
shared_mutex m_cache_mutex ;
2018-09-22 02:14:26 +02:00
ranged_storage m_storage ;
2017-11-03 12:16:55 +01:00
std : : unordered_multimap < u32 , std : : pair < deferred_subresource , image_view_type > > m_temporary_subresource_cache ;
2019-10-17 21:52:53 +02:00
std : : vector < image_view_type > m_uncached_subresources ;
2018-10-28 14:59:39 +01:00
predictor_type m_predictor ;
2017-09-08 16:52:13 +02:00
2020-12-06 13:15:19 +01:00
atomic_t < u64 > m_cache_update_tag = { 0 } ;
2017-10-28 21:17:27 +02:00
2018-09-22 02:14:26 +02:00
address_range read_only_range ;
address_range no_access_range ;
2017-09-08 16:52:13 +02:00
2018-02-23 20:49:59 +01:00
//Map of messages to only emit once
2018-08-24 16:38:17 +02:00
std : : unordered_set < std : : string > m_once_only_messages_set ;
2017-11-02 07:29:17 +01:00
2018-01-31 17:11:03 +01:00
//Set when a shader read-only texture data suddenly becomes contested, usually by fbo memory
bool read_only_tex_invalidate = false ;
2018-02-03 09:37:42 +01:00
//Store of all objects in a flush_always state. A lazy readback is attempted every draw call
2018-09-22 02:14:26 +02:00
std : : unordered_map < address_range , section_storage_type * > m_flush_always_cache ;
2018-02-03 09:37:42 +01:00
u64 m_flush_always_update_timestamp = 0 ;
2017-09-08 16:52:13 +02:00
//Memory usage
2018-09-22 02:14:26 +02:00
const u32 m_max_zombie_objects = 64 ; //Limit on how many texture objects to keep around for reuse after they are invalidated
2017-11-02 07:29:17 +01:00
2018-02-11 13:48:36 +01:00
//Other statistics
2020-12-06 13:15:19 +01:00
atomic_t < u32 > m_flushes_this_frame = { 0 } ;
atomic_t < u32 > m_misses_this_frame = { 0 } ;
atomic_t < u32 > m_speculations_this_frame = { 0 } ;
atomic_t < u32 > m_unavoidable_hard_faults_this_frame = { 0 } ;
2020-12-14 19:29:07 +01:00
atomic_t < u32 > m_texture_upload_calls_this_frame = { 0 } ;
atomic_t < u32 > m_texture_upload_misses_this_frame = { 0 } ;
2018-12-10 00:22:02 +01:00
static const u32 m_predict_max_flushes_per_frame = 50 ; // Above this number the predictions are disabled
2018-02-11 13:48:36 +01:00
2018-09-22 02:14:26 +02:00
// Invalidation
static const bool invalidation_ignore_unsynchronized = true ; // If true, unsynchronized sections don't get forcefully flushed unless they overlap the fault range
static const bool invalidation_keep_ro_during_read = true ; // If true, RO sections are not invalidated during read faults
2018-10-28 14:59:39 +01:00
2018-09-22 02:14:26 +02:00
/**
* Virtual Methods
*/
2018-03-18 12:40:26 +01:00
virtual image_view_type create_temporary_subresource_view ( commandbuffer_type & , image_resource_type * src , u32 gcm_format , u16 x , u16 y , u16 w , u16 h , const texture_channel_remap_t & remap_vector ) = 0 ;
virtual image_view_type create_temporary_subresource_view ( commandbuffer_type & , image_storage_type * src , u32 gcm_format , u16 x , u16 y , u16 w , u16 h , const texture_channel_remap_t & remap_vector ) = 0 ;
2019-10-17 20:35:04 +02:00
virtual void release_temporary_subresource ( image_view_type rsc ) = 0 ;
2022-01-09 21:07:18 +01:00
virtual section_storage_type * create_new_texture ( commandbuffer_type & , const address_range & rsx_range , u16 width , u16 height , u16 depth , u16 mipmaps , u32 pitch , u32 gcm_format ,
2021-05-28 00:44:07 +02:00
rsx : : texture_upload_context context , rsx : : texture_dimension_extended type , bool swizzled , component_order swizzle_flags , rsx : : flags32_t flags ) = 0 ;
2022-01-09 21:07:18 +01:00
virtual section_storage_type * upload_image_from_cpu ( commandbuffer_type & , const address_range & rsx_range , u16 width , u16 height , u16 depth , u16 mipmaps , u32 pitch , u32 gcm_format , texture_upload_context context ,
2020-08-15 23:33:34 +02:00
const std : : vector < rsx : : subresource_layout > & subresource_layout , rsx : : texture_dimension_extended type , bool swizzled ) = 0 ;
2019-09-04 21:19:58 +02:00
virtual section_storage_type * create_nul_section ( commandbuffer_type & , const address_range & rsx_range , bool memory_load ) = 0 ;
2021-05-28 00:44:07 +02:00
virtual void set_component_order ( section_storage_type & section , u32 gcm_format , component_order expected ) = 0 ;
2020-12-14 18:24:28 +01:00
virtual void insert_texture_barrier ( commandbuffer_type & , image_storage_type * tex , bool strong_ordering = true ) = 0 ;
2018-03-30 12:28:46 +02:00
virtual image_view_type generate_cubemap_from_images ( commandbuffer_type & , u32 gcm_format , u16 size , const std : : vector < copy_region_descriptor > & sources , const texture_channel_remap_t & remap_vector ) = 0 ;
virtual image_view_type generate_3d_from_2d_images ( commandbuffer_type & , u32 gcm_format , u16 width , u16 height , u16 depth , const std : : vector < copy_region_descriptor > & sources , const texture_channel_remap_t & remap_vector ) = 0 ;
2018-03-18 12:40:26 +01:00
virtual image_view_type generate_atlas_from_images ( commandbuffer_type & , u32 gcm_format , u16 width , u16 height , const std : : vector < copy_region_descriptor > & sections_to_copy , const texture_channel_remap_t & remap_vector ) = 0 ;
2019-10-09 21:29:23 +02:00
virtual image_view_type generate_2d_mipmaps_from_images ( commandbuffer_type & , u32 gcm_format , u16 width , u16 height , const std : : vector < copy_region_descriptor > & sections_to_copy , const texture_channel_remap_t & remap_vector ) = 0 ;
2018-02-21 11:46:23 +01:00
virtual void update_image_contents ( commandbuffer_type & , image_view_type dst , image_resource_type src , u16 width , u16 height ) = 0 ;
2018-02-23 20:49:59 +01:00
virtual bool render_target_format_is_compatible ( image_storage_type * tex , u32 gcm_format ) = 0 ;
2019-03-16 10:14:11 +01:00
virtual void prepare_for_dma_transfers ( commandbuffer_type & ) = 0 ;
virtual void cleanup_after_dma_transfers ( commandbuffer_type & ) = 0 ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
public :
virtual void destroy ( ) = 0 ;
virtual bool is_depth_texture ( u32 , u32 ) = 0 ;
2018-11-01 02:31:12 +01:00
virtual void on_section_destroyed ( section_storage_type & /*section*/ )
2018-10-28 14:59:39 +01:00
{ }
2017-09-19 14:46:16 +02:00
2018-10-28 14:59:39 +01:00
protected :
2018-09-22 02:14:26 +02:00
/**
* Helpers
*/
2017-10-28 21:17:27 +02:00
inline void update_cache_tag ( )
{
2019-02-02 20:44:18 +01:00
m_cache_update_tag = rsx : : get_shared_tag ( ) ;
2017-10-28 21:17:27 +02:00
}
2020-12-18 08:39:54 +01:00
template < typename CharT , usz N , typename . . . Args >
2020-02-04 19:37:00 +01:00
void emit_once ( bool error , const CharT ( & fmt ) [ N ] , const Args & . . . params )
2018-02-23 20:49:59 +01:00
{
2018-08-24 16:38:17 +02:00
const auto result = m_once_only_messages_set . emplace ( fmt : : format ( fmt , params . . . ) ) ;
if ( ! result . second )
2018-02-23 20:49:59 +01:00
return ;
if ( error )
2020-02-01 09:07:25 +01:00
rsx_log . error ( " %s " , * result . first ) ;
2018-02-23 20:49:59 +01:00
else
2020-02-01 09:07:25 +01:00
rsx_log . warning ( " %s " , * result . first ) ;
2018-02-23 20:49:59 +01:00
}
2020-12-18 08:39:54 +01:00
template < typename CharT , usz N , typename . . . Args >
2020-02-04 19:37:00 +01:00
void err_once ( const CharT ( & fmt ) [ N ] , const Args & . . . params )
2018-02-23 20:49:59 +01:00
{
2019-03-16 10:14:11 +01:00
emit_once ( true , fmt , params . . . ) ;
2018-02-23 20:49:59 +01:00
}
2020-12-18 08:39:54 +01:00
template < typename CharT , usz N , typename . . . Args >
2020-02-04 19:37:00 +01:00
void warn_once ( const CharT ( & fmt ) [ N ] , const Args & . . . params )
2018-02-23 20:49:59 +01:00
{
2019-03-16 10:14:11 +01:00
emit_once ( false , fmt , params . . . ) ;
2018-02-23 20:49:59 +01:00
}
2018-09-22 02:14:26 +02:00
/**
* Internal implementation methods and helpers
*/
2017-10-21 23:12:32 +02:00
2018-09-22 02:14:26 +02:00
inline bool region_intersects_cache ( const address_range & test_range , bool is_writing )
2017-10-26 15:20:09 +02:00
{
2018-10-19 00:22:00 +02:00
AUDIT ( test_range . valid ( ) ) ;
2017-10-26 15:20:09 +02:00
2018-09-22 02:14:26 +02:00
// Quick range overlaps with cache tests
2017-10-28 10:45:43 +02:00
if ( ! is_writing )
{
2018-09-22 02:14:26 +02:00
if ( ! no_access_range . valid ( ) | | ! test_range . overlaps ( no_access_range ) )
2017-10-28 10:45:43 +02:00
return false ;
}
else
{
2018-09-22 02:14:26 +02:00
if ( ! read_only_range . valid ( ) | | ! test_range . overlaps ( read_only_range ) )
2017-10-28 10:45:43 +02:00
{
//Doesnt fall in the read_only textures range; check render targets
2018-09-22 02:14:26 +02:00
if ( ! no_access_range . valid ( ) | | ! test_range . overlaps ( no_access_range ) )
2017-10-28 10:45:43 +02:00
return false ;
}
}
2018-09-22 02:14:26 +02:00
// Check that there is at least one valid (locked) section in the test_range
reader_lock lock ( m_cache_mutex ) ;
if ( m_storage . range_begin ( test_range , locked_range , true ) = = m_storage . range_end ( ) )
return false ;
// We do intersect the cache
2017-10-28 10:45:43 +02:00
return true ;
}
2018-09-22 02:14:26 +02:00
/**
* Section invalidation
*/
private :
2018-09-19 00:21:57 +02:00
template < typename . . . Args >
2018-12-29 14:28:12 +01:00
void flush_set ( commandbuffer_type & cmd , thrashed_set & data , Args & & . . . extras )
2018-09-19 00:21:57 +02:00
{
2018-09-22 02:14:26 +02:00
AUDIT ( ! data . flushed ) ;
2018-09-19 00:21:57 +02:00
if ( data . sections_to_flush . size ( ) > 1 )
{
// Sort with oldest data first
// Ensures that new data tramples older data
std : : sort ( data . sections_to_flush . begin ( ) , data . sections_to_flush . end ( ) , [ ] ( const auto & a , const auto & b )
{
return ( a - > last_write_tag < b - > last_write_tag ) ;
} ) ;
}
2019-03-16 10:14:11 +01:00
rsx : : simple_array < section_storage_type * > sections_to_transfer ;
2018-09-19 00:21:57 +02:00
for ( auto & surface : data . sections_to_flush )
{
2019-03-16 10:14:11 +01:00
if ( ! surface - > is_synchronized ( ) )
{
sections_to_transfer . push_back ( surface ) ;
}
else if ( surface - > get_memory_read_flags ( ) = = rsx : : memory_read_flags : : flush_always )
2018-09-19 00:21:57 +02:00
{
// This region is set to always read from itself (unavoidable hard sync)
const auto ROP_timestamp = rsx : : get_current_renderer ( ) - > ROP_sync_timestamp ;
2019-03-16 10:14:11 +01:00
if ( ROP_timestamp > surface - > get_sync_timestamp ( ) )
2018-09-19 00:21:57 +02:00
{
2019-03-16 10:14:11 +01:00
sections_to_transfer . push_back ( surface ) ;
2018-09-19 00:21:57 +02:00
}
}
2019-03-16 10:14:11 +01:00
}
if ( ! sections_to_transfer . empty ( ) )
{
// Batch all hard faults together
prepare_for_dma_transfers ( cmd ) ;
for ( auto & surface : sections_to_transfer )
{
surface - > copy_texture ( cmd , true , std : : forward < Args > ( extras ) . . . ) ;
}
cleanup_after_dma_transfers ( cmd ) ;
}
2018-09-19 00:21:57 +02:00
2019-03-16 10:14:11 +01:00
for ( auto & surface : data . sections_to_flush )
{
surface - > flush ( ) ;
2018-11-01 02:31:12 +01:00
// Exclude this region when flushing other sections that should not trample it
// If we overlap an excluded RO, set it as dirty
for ( auto & other : data . sections_to_exclude )
{
AUDIT ( other ! = surface ) ;
if ( ! other - > is_flushable ( ) )
{
2021-12-24 12:11:47 +01:00
if ( other - > overlaps ( * surface , section_bounds : : confirmed_range ) )
2018-11-01 02:31:12 +01:00
{
2021-12-24 12:11:47 +01:00
// This should never happen. It will raise exceptions later due to a dirty region being locked
rsx_log . error ( " Excluded region overlaps with flushed surface! " ) ;
2018-11-01 02:31:12 +01:00
other - > set_dirty ( true ) ;
}
}
else if ( surface - > last_write_tag > other - > last_write_tag )
{
other - > add_flush_exclusion ( surface - > get_confirmed_range ( ) ) ;
}
}
2018-09-19 00:21:57 +02:00
}
2020-10-20 22:38:05 +02:00
// Resync any exclusions that do not require flushing
std : : vector < section_storage_type * > surfaces_to_inherit ;
for ( auto & surface : data . sections_to_exclude )
{
if ( surface - > get_context ( ) ! = texture_upload_context : : framebuffer_storage )
{
continue ;
}
// Check for any 'newer' flushed overlaps. Memory must be re-acquired to avoid holding stale contents
// Note that the surface cache inheritance will minimize the impact
surfaces_to_inherit . clear ( ) ;
for ( auto & flushed_surface : data . sections_to_flush )
{
if ( flushed_surface - > get_context ( ) ! = texture_upload_context : : framebuffer_storage | |
flushed_surface - > last_write_tag < = surface - > last_write_tag | |
! flushed_surface - > get_confirmed_range ( ) . overlaps ( surface - > get_confirmed_range ( ) ) )
{
continue ;
}
surfaces_to_inherit . push_back ( flushed_surface ) ;
}
surface - > sync_surface_memory ( surfaces_to_inherit ) ;
}
2018-09-22 02:14:26 +02:00
data . flushed = true ;
2018-09-19 00:21:57 +02:00
}
2018-09-22 02:14:26 +02:00
// Merges the protected ranges of the sections in "sections" into "result"
void merge_protected_ranges ( address_range_vector & result , const std : : vector < section_storage_type * > & sections )
{
result . reserve ( result . size ( ) + sections . size ( ) ) ;
// Copy ranges to result, merging them if possible
for ( const auto & section : sections )
{
2021-01-28 19:11:53 +01:00
ensure ( section - > is_locked ( true ) ) ;
2018-09-22 02:14:26 +02:00
const auto & new_range = section - > get_locked_range ( ) ;
2018-10-19 00:22:00 +02:00
AUDIT ( new_range . is_page_range ( ) ) ;
2018-09-22 02:14:26 +02:00
result . merge ( new_range ) ;
}
}
// NOTE: It is *very* important that data contains exclusions for *all* sections that overlap sections_to_unprotect/flush
// Otherwise the page protections will end up incorrect and things will break!
2018-08-22 15:59:15 +02:00
void unprotect_set ( thrashed_set & data )
{
2018-09-22 02:14:26 +02:00
auto protect_ranges = [ this ] ( address_range_vector & _set , utils : : protection _prot )
2018-08-22 15:59:15 +02:00
{
2018-09-22 02:14:26 +02:00
u32 count = 0 ;
for ( auto & range : _set )
2018-08-22 15:59:15 +02:00
{
2018-09-22 02:14:26 +02:00
if ( range . valid ( ) )
{
rsx : : memory_protect ( range , _prot ) ;
count + + ;
}
2018-08-22 15:59:15 +02:00
}
2020-02-01 09:07:25 +01:00
//rsx_log.error("Set protection of %d blocks to 0x%x", count, static_cast<u32>(prot));
2018-08-22 15:59:15 +02:00
} ;
auto discard_set = [ this ] ( std : : vector < section_storage_type * > & _set )
{
2018-09-22 02:14:26 +02:00
for ( auto * section : _set )
2018-08-22 15:59:15 +02:00
{
2020-12-09 08:47:45 +01:00
ensure ( section - > is_flushed ( ) | | section - > is_dirty ( ) ) ;
2018-08-22 15:59:15 +02:00
2018-09-22 02:14:26 +02:00
section - > discard ( /*set_dirty*/ false ) ;
2018-08-22 15:59:15 +02:00
}
} ;
2018-09-22 02:14:26 +02:00
// Sanity checks
AUDIT ( data . fault_range . is_page_range ( ) ) ;
AUDIT ( data . invalidate_range . is_page_range ( ) ) ;
AUDIT ( data . is_flushed ( ) ) ;
// Merge ranges to unprotect
address_range_vector ranges_to_unprotect ;
address_range_vector ranges_to_protect_ro ;
ranges_to_unprotect . reserve ( data . sections_to_unprotect . size ( ) + data . sections_to_flush . size ( ) + data . sections_to_exclude . size ( ) ) ;
merge_protected_ranges ( ranges_to_unprotect , data . sections_to_unprotect ) ;
merge_protected_ranges ( ranges_to_unprotect , data . sections_to_flush ) ;
AUDIT ( ! ranges_to_unprotect . empty ( ) ) ;
// Apply exclusions and collect ranges of excluded pages that need to be reprotected RO (i.e. only overlap RO regions)
if ( ! data . sections_to_exclude . empty ( ) )
2018-08-22 15:59:15 +02:00
{
2018-09-22 02:14:26 +02:00
ranges_to_protect_ro . reserve ( data . sections_to_exclude . size ( ) ) ;
u32 no_access_count = 0 ;
for ( const auto & excluded : data . sections_to_exclude )
2018-08-22 15:59:15 +02:00
{
2021-01-28 19:11:53 +01:00
ensure ( excluded - > is_locked ( true ) ) ;
2018-09-22 02:14:26 +02:00
address_range exclusion_range = excluded - > get_locked_range ( ) ;
// We need to make sure that the exclusion range is *inside* invalidate range
exclusion_range . intersect ( data . invalidate_range ) ;
// Sanity checks
2018-10-19 00:22:00 +02:00
AUDIT ( exclusion_range . is_page_range ( ) ) ;
2021-01-18 11:49:59 +01:00
AUDIT ( ( data . cause . is_read ( ) & & ! excluded - > is_flushable ( ) ) | | data . cause . skip_fbos ( ) | | ! exclusion_range . overlaps ( data . fault_range ) ) ;
2018-09-22 02:14:26 +02:00
// Apply exclusion
ranges_to_unprotect . exclude ( exclusion_range ) ;
// Keep track of RO exclusions
// TODO ruipin: Bug here, we cannot add the whole exclusion range to ranges_to_reprotect, only the part inside invalidate_range
utils : : protection prot = excluded - > get_protection ( ) ;
if ( prot = = utils : : protection : : ro )
{
ranges_to_protect_ro . merge ( exclusion_range ) ;
}
else if ( prot = = utils : : protection : : no )
{
no_access_count + + ;
}
else
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Unreachable " ) ;
2018-09-22 02:14:26 +02:00
}
2018-08-22 15:59:15 +02:00
}
2018-09-22 02:14:26 +02:00
// Exclude NA ranges from ranges_to_reprotect_ro
if ( no_access_count > 0 & & ! ranges_to_protect_ro . empty ( ) )
2018-08-22 15:59:15 +02:00
{
2018-09-22 02:14:26 +02:00
for ( auto & exclusion : data . sections_to_exclude )
2018-08-22 15:59:15 +02:00
{
2018-09-22 02:14:26 +02:00
if ( exclusion - > get_protection ( ) ! = utils : : protection : : ro )
{
ranges_to_protect_ro . exclude ( exclusion - > get_locked_range ( ) ) ;
}
2018-08-22 15:59:15 +02:00
}
}
2018-09-22 02:14:26 +02:00
}
2018-10-19 00:22:00 +02:00
AUDIT ( ! ranges_to_unprotect . empty ( ) ) ;
2018-09-22 02:14:26 +02:00
// Exclude the fault range if told to do so (this means the fault_range got unmapped or is otherwise invalid)
2018-10-19 00:22:00 +02:00
if ( data . cause . keep_fault_range_protection ( ) )
2018-09-22 02:14:26 +02:00
{
ranges_to_unprotect . exclude ( data . fault_range ) ;
ranges_to_protect_ro . exclude ( data . fault_range ) ;
2018-08-22 15:59:15 +02:00
2018-09-22 02:14:26 +02:00
AUDIT ( ! ranges_to_unprotect . overlaps ( data . fault_range ) ) ;
AUDIT ( ! ranges_to_protect_ro . overlaps ( data . fault_range ) ) ;
2018-08-22 15:59:15 +02:00
}
2018-09-22 02:14:26 +02:00
else
{
2018-10-19 00:22:00 +02:00
AUDIT ( ranges_to_unprotect . inside ( data . invalidate_range ) ) ;
AUDIT ( ranges_to_protect_ro . inside ( data . invalidate_range ) ) ;
2018-09-22 02:14:26 +02:00
}
2018-10-19 00:22:00 +02:00
AUDIT ( ! ranges_to_protect_ro . overlaps ( ranges_to_unprotect ) ) ;
2018-09-22 02:14:26 +02:00
// Unprotect and discard
protect_ranges ( ranges_to_unprotect , utils : : protection : : rw ) ;
protect_ranges ( ranges_to_protect_ro , utils : : protection : : ro ) ;
discard_set ( data . sections_to_unprotect ) ;
discard_set ( data . sections_to_flush ) ;
# ifdef TEXTURE_CACHE_DEBUG
// Check that the cache looks sane
data . check_post_sanity ( ) ;
# endif // TEXTURE_CACHE_DEBUG
2018-08-22 15:59:15 +02:00
}
2018-09-22 02:14:26 +02:00
// Return a set containing all sections that should be flushed/unprotected/reprotected
2020-12-06 13:15:19 +01:00
atomic_t < u64 > m_last_section_cache_tag = 0 ;
2018-10-19 00:22:00 +02:00
intersecting_set get_intersecting_set ( const address_range & fault_range )
2017-09-14 13:37:14 +02:00
{
2018-10-19 00:22:00 +02:00
AUDIT ( fault_range . is_page_range ( ) ) ;
2018-09-22 02:14:26 +02:00
const u64 cache_tag = + + m_last_section_cache_tag ;
intersecting_set result = { } ;
address_range & invalidate_range = result . invalidate_range ;
invalidate_range = fault_range ; // Sections fully inside this range will be invalidated, others will be deemed false positives
// Loop through cache and find pages that overlap the invalidate_range
2021-05-22 20:46:10 +02:00
u32 last_dirty_block = - 1 ;
2018-08-12 01:30:23 +02:00
bool repeat_loop = false ;
2017-10-24 12:59:46 +02:00
2021-01-25 17:31:00 +01:00
auto It = m_storage . range_begin ( invalidate_range , locked_range , true ) ; // will iterate through locked sections only
2018-09-22 02:14:26 +02:00
while ( It ! = m_storage . range_end ( ) )
2017-09-14 13:37:14 +02:00
{
2018-09-22 02:14:26 +02:00
const u32 base = It . get_block ( ) . get_start ( ) ;
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
// On the last loop, we stop once we're done with the last dirty block
if ( ! repeat_loop & & base > last_dirty_block ) // note: blocks are iterated in order from lowest to highest base address
break ;
auto & tex = * It ;
AUDIT ( tex . is_locked ( ) ) ; // we should be iterating locked sections only, but just to make sure...
2021-05-22 20:46:10 +02:00
AUDIT ( tex . cache_tag ! = cache_tag | | last_dirty_block ! = umax ) ; // cache tag should not match during the first loop
2018-09-22 02:14:26 +02:00
if ( tex . cache_tag ! = cache_tag ) //flushable sections can be 'clean' but unlocked. TODO: Handle this better
2017-09-15 00:32:23 +02:00
{
2018-09-22 02:14:26 +02:00
const rsx : : section_bounds bounds = tex . get_overlap_test_bounds ( ) ;
2021-01-25 17:31:00 +01:00
if ( locked_range = = bounds | | tex . overlaps ( invalidate_range , bounds ) )
2018-08-11 23:50:57 +02:00
{
2018-09-22 02:14:26 +02:00
const auto new_range = tex . get_min_max ( invalidate_range , bounds ) . to_page_range ( ) ;
AUDIT ( new_range . is_page_range ( ) & & invalidate_range . inside ( new_range ) ) ;
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
// The various chaining policies behave differently
bool extend_invalidate_range = tex . overlaps ( fault_range , bounds ) ;
// Extend the various ranges
if ( extend_invalidate_range & & new_range ! = invalidate_range )
2018-08-12 01:30:23 +02:00
{
2018-09-22 02:14:26 +02:00
if ( new_range . end > invalidate_range . end )
It . set_end ( new_range . end ) ;
2018-05-10 13:50:32 +02:00
2018-09-22 02:14:26 +02:00
invalidate_range = new_range ;
repeat_loop = true ; // we will need to repeat the loop again
last_dirty_block = base ; // stop the repeat loop once we finish this block
}
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
// Add texture to result, and update its cache tag
tex . cache_tag = cache_tag ;
result . sections . push_back ( & tex ) ;
if ( tex . is_flushable ( ) )
{
result . has_flushables = true ;
2017-09-14 13:37:14 +02:00
}
}
}
2018-08-12 01:30:23 +02:00
// Iterate
It + + ;
// repeat_loop==true means some blocks are still dirty and we need to repeat the loop again
2018-09-22 02:14:26 +02:00
if ( repeat_loop & & It = = m_storage . range_end ( ) )
2017-09-14 13:37:14 +02:00
{
2021-01-25 17:31:00 +01:00
It = m_storage . range_begin ( invalidate_range , locked_range , true ) ;
2018-08-12 01:30:23 +02:00
repeat_loop = false ;
2017-09-14 13:37:14 +02:00
}
}
2018-10-19 00:22:00 +02:00
AUDIT ( result . invalidate_range . is_page_range ( ) ) ;
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// naive check that sections are not duplicated in the results
for ( auto & section1 : result . sections )
{
2020-12-18 08:39:54 +01:00
usz count = 0 ;
2018-09-22 02:14:26 +02:00
for ( auto & section2 : result . sections )
{
if ( section1 = = section2 ) count + + ;
}
2020-12-09 08:47:45 +01:00
ensure ( count = = 1 ) ;
2018-09-22 02:14:26 +02:00
}
# endif //TEXTURE_CACHE_DEBUG
2017-10-21 23:12:32 +02:00
return result ;
2017-09-14 13:37:14 +02:00
}
2018-09-22 02:14:26 +02:00
2017-10-21 23:12:32 +02:00
//Invalidate range base implementation
2017-09-14 13:37:14 +02:00
template < typename . . . Args >
2018-12-29 14:28:12 +01:00
thrashed_set invalidate_range_impl_base ( commandbuffer_type & cmd , const address_range & fault_range_in , invalidation_cause cause , Args & & . . . extras )
2017-09-14 13:37:14 +02:00
{
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// Check that the cache has the correct protections
tex_cache_checker . verify ( ) ;
# endif // TEXTURE_CACHE_DEBUG
2017-10-28 10:45:43 +02:00
2018-10-19 00:22:00 +02:00
AUDIT ( cause . valid ( ) ) ;
AUDIT ( fault_range_in . valid ( ) ) ;
2018-09-22 02:14:26 +02:00
address_range fault_range = fault_range_in . to_page_range ( ) ;
2017-09-14 13:37:14 +02:00
2018-11-01 02:31:12 +01:00
intersecting_set trampled_set = std : : move ( get_intersecting_set ( fault_range ) ) ;
2017-12-04 17:30:09 +01:00
2018-09-22 02:14:26 +02:00
thrashed_set result = { } ;
result . cause = cause ;
result . fault_range = fault_range ;
result . invalidate_range = trampled_set . invalidate_range ;
2017-12-04 17:30:09 +01:00
2018-10-19 00:22:00 +02:00
// Fast code-path for keeping the fault range protection when not flushing anything
if ( cause . keep_fault_range_protection ( ) & & cause . skip_flush ( ) & & ! trampled_set . sections . empty ( ) )
2018-09-22 02:14:26 +02:00
{
2020-12-09 08:47:45 +01:00
ensure ( cause ! = invalidation_cause : : committed_as_fbo ) ;
2019-03-02 13:14:22 +01:00
2018-09-22 02:14:26 +02:00
// We discard all sections fully inside fault_range
for ( auto & obj : trampled_set . sections )
2017-12-04 17:30:09 +01:00
{
2018-09-22 02:14:26 +02:00
auto & tex = * obj ;
2018-12-10 00:22:02 +01:00
if ( tex . overlaps ( fault_range , section_bounds : : locked_range ) )
2018-09-22 02:14:26 +02:00
{
2018-12-10 00:22:02 +01:00
if ( cause = = invalidation_cause : : superseded_by_fbo & &
2019-10-13 21:56:56 +02:00
tex . is_flushable ( ) & &
2018-12-10 00:22:02 +01:00
tex . get_section_base ( ) ! = fault_range_in . start )
{
2019-10-13 21:56:56 +02:00
// HACK: When being superseded by an fbo, we preserve overlapped flushables unless the start addresses match
2018-12-10 00:22:02 +01:00
continue ;
}
else if ( tex . inside ( fault_range , section_bounds : : locked_range ) )
{
// Discard - this section won't be needed any more
tex . discard ( /* set_dirty */ true ) ;
2021-01-28 19:53:49 +01:00
result . invalidate_samplers = true ;
2018-12-10 00:22:02 +01:00
}
else if ( g_cfg . video . strict_texture_flushing & & tex . is_flushable ( ) )
2017-12-04 17:30:09 +01:00
{
2018-11-01 02:31:12 +01:00
tex . add_flush_exclusion ( fault_range ) ;
}
else
{
tex . set_dirty ( true ) ;
2021-01-28 19:53:49 +01:00
result . invalidate_samplers = true ;
2017-12-04 17:30:09 +01:00
}
2022-03-27 19:25:34 +02:00
if ( tex . is_dirty ( ) & & tex . get_context ( ) = = rsx : : texture_upload_context : : framebuffer_storage )
{
// Make sure the region is not going to get immediately reprotected
m_flush_always_cache . erase ( tex . get_section_range ( ) ) ;
}
2017-12-04 17:30:09 +01:00
}
2018-09-22 02:14:26 +02:00
}
2018-08-22 15:59:15 +02:00
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// Notify the checker that fault_range got discarded
tex_cache_checker . discard ( fault_range ) ;
# endif
// If invalidate_range is fault_range, we can stop now
const address_range invalidate_range = trampled_set . invalidate_range ;
if ( invalidate_range = = fault_range )
{
result . violation_handled = true ;
2021-01-28 19:53:49 +01:00
result . invalidate_samplers = true ;
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// Post-check the result
result . check_post_sanity ( ) ;
# endif
return result ;
2017-12-04 17:30:09 +01:00
}
2018-09-22 02:14:26 +02:00
AUDIT ( fault_range . inside ( invalidate_range ) ) ;
}
2018-08-22 15:59:15 +02:00
2018-09-22 02:14:26 +02:00
// Decide which sections to flush, unprotect, and exclude
if ( ! trampled_set . sections . empty ( ) )
{
update_cache_tag ( ) ;
2018-05-10 13:50:32 +02:00
2018-09-22 02:14:26 +02:00
for ( auto & obj : trampled_set . sections )
{
auto & tex = * obj ;
2018-05-10 13:50:32 +02:00
2021-01-28 19:53:49 +01:00
if ( ! tex . is_locked ( ) )
2018-10-19 00:22:00 +02:00
continue ;
2018-09-22 02:14:26 +02:00
const rsx : : section_bounds bounds = tex . get_overlap_test_bounds ( ) ;
2018-12-10 00:22:02 +01:00
const bool overlaps_fault_range = tex . overlaps ( fault_range , bounds ) ;
2018-09-22 02:14:26 +02:00
if (
// RO sections during a read invalidation can be ignored (unless there are flushables in trampled_set, since those could overwrite RO data)
2018-11-01 02:31:12 +01:00
( invalidation_keep_ro_during_read & & ! trampled_set . has_flushables & & cause . is_read ( ) & & ! tex . is_flushable ( ) ) | |
2018-09-22 02:14:26 +02:00
// Sections that are not fully contained in invalidate_range can be ignored
! tex . inside ( trampled_set . invalidate_range , bounds ) | |
2018-10-19 00:22:00 +02:00
// Unsynchronized sections (or any flushable when skipping flushes) that do not overlap the fault range directly can also be ignored
2018-12-10 00:22:02 +01:00
( invalidation_ignore_unsynchronized & & tex . is_flushable ( ) & & ( cause . skip_flush ( ) | | ! tex . is_synchronized ( ) ) & & ! overlaps_fault_range ) | |
2019-10-13 21:56:56 +02:00
// HACK: When being superseded by an fbo, we preserve other overlapped flushables unless the start addresses match
// If region is committed as fbo, all non-flushable data is removed but all flushables in the region must be preserved if possible
( overlaps_fault_range & & tex . is_flushable ( ) & & cause . skip_fbos ( ) & & tex . get_section_base ( ) ! = fault_range_in . start )
2018-09-22 02:14:26 +02:00
)
{
// False positive
2021-01-28 19:53:49 +01:00
if ( tex . is_locked ( true ) )
{
// Do not exclude hashed pages from unprotect! They will cause protection holes
result . sections_to_exclude . push_back ( & tex ) ;
}
2018-09-22 02:14:26 +02:00
continue ;
}
if ( tex . is_flushable ( ) )
{
2018-10-19 00:22:00 +02:00
// Write if and only if no one else has trashed section memory already
// TODO: Proper section management should prevent this from happening
// TODO: Blit engine section merge support and/or partial texture memory buffering
2021-01-25 17:31:00 +01:00
if ( tex . is_dirty ( ) )
2018-09-22 02:14:26 +02:00
{
2018-10-19 00:22:00 +02:00
// Contents clobbered, destroy this
2018-09-22 02:14:26 +02:00
if ( ! tex . is_dirty ( ) )
2018-05-10 13:50:32 +02:00
{
2018-09-22 02:14:26 +02:00
tex . set_dirty ( true ) ;
2017-11-04 14:29:54 +01:00
}
2018-05-10 13:50:32 +02:00
2018-09-22 02:14:26 +02:00
result . sections_to_unprotect . push_back ( & tex ) ;
2018-05-10 13:50:32 +02:00
}
2018-08-22 15:59:15 +02:00
else
2018-05-10 13:50:32 +02:00
{
2018-09-22 02:14:26 +02:00
result . sections_to_flush . push_back ( & tex ) ;
}
2018-08-13 21:40:32 +02:00
2018-09-22 02:14:26 +02:00
continue ;
}
else
{
2018-10-19 00:22:00 +02:00
// deferred_flush = true and not synchronized
2018-09-22 02:14:26 +02:00
if ( ! tex . is_dirty ( ) )
{
2018-10-19 00:22:00 +02:00
AUDIT ( tex . get_memory_read_flags ( ) ! = memory_read_flags : : flush_always ) ;
2018-09-22 02:14:26 +02:00
tex . set_dirty ( true ) ;
2017-11-04 14:29:54 +01:00
}
2018-09-22 02:14:26 +02:00
2021-01-28 19:53:49 +01:00
if ( tex . is_locked ( true ) )
{
result . sections_to_unprotect . push_back ( & tex ) ;
}
else
{
// No need to waste resources on hashed section, just discard immediately
tex . discard ( true ) ;
result . invalidate_samplers = true ;
}
2018-09-22 02:14:26 +02:00
continue ;
2017-11-04 14:29:54 +01:00
}
2017-10-21 23:12:32 +02:00
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Unreachable " ) ;
2018-09-22 02:14:26 +02:00
}
2017-10-21 23:12:32 +02:00
2018-08-22 15:59:15 +02:00
2018-09-22 02:14:26 +02:00
result . violation_handled = true ;
# ifdef TEXTURE_CACHE_DEBUG
// Check that result makes sense
result . check_pre_sanity ( ) ;
# endif // TEXTURE_CACHE_DEBUG
const bool has_flushables = ! result . sections_to_flush . empty ( ) ;
const bool has_unprotectables = ! result . sections_to_unprotect . empty ( ) ;
2017-09-14 13:37:14 +02:00
2018-10-19 00:22:00 +02:00
if ( cause . deferred_flush ( ) & & has_flushables )
2017-10-21 23:12:32 +02:00
{
2018-09-22 02:14:26 +02:00
// There is something to flush, but we've been asked to defer it
result . num_flushable = static_cast < int > ( result . sections_to_flush . size ( ) ) ;
2020-12-06 13:15:19 +01:00
result . cache_tag = m_cache_update_tag . load ( ) ;
2018-09-22 02:14:26 +02:00
return result ;
}
else if ( has_flushables | | has_unprotectables )
{
2018-10-19 00:22:00 +02:00
AUDIT ( ! has_flushables | | ! cause . deferred_flush ( ) ) ;
2018-09-22 02:14:26 +02:00
// We have something to flush and are allowed to flush now
// or there is nothing to flush but we have something to unprotect
2018-10-19 00:22:00 +02:00
if ( has_flushables & & ! cause . skip_flush ( ) )
2018-09-19 00:21:57 +02:00
{
2018-12-29 14:28:12 +01:00
flush_set ( cmd , result , std : : forward < Args > ( extras ) . . . ) ;
2018-09-19 00:21:57 +02:00
}
2018-09-22 02:14:26 +02:00
unprotect_set ( result ) ;
2018-10-19 00:22:00 +02:00
// Everything has been handled
2018-09-22 02:14:26 +02:00
result . clear_sections ( ) ;
2017-10-27 15:52:27 +02:00
}
2018-09-22 02:14:26 +02:00
else
{
2018-12-10 00:22:02 +01:00
// This is a read and all overlapping sections were RO and were excluded (except for cause == superseded_by_fbo)
2021-01-18 11:49:59 +01:00
AUDIT ( cause . skip_fbos ( ) | | ( cause . is_read ( ) & & ! result . sections_to_exclude . empty ( ) ) ) ;
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
// We did not handle this violation
result . clear_sections ( ) ;
result . violation_handled = false ;
}
2021-01-28 19:53:49 +01:00
result . invalidate_samplers | = result . violation_handled ;
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// Post-check the result
result . check_post_sanity ( ) ;
# endif // TEXTURE_CACHE_DEBUG
2018-09-19 00:21:57 +02:00
2017-10-27 23:32:27 +02:00
return result ;
2017-09-15 00:32:23 +02:00
}
2017-10-27 23:32:27 +02:00
return { } ;
2017-10-21 23:12:32 +02:00
}
2017-09-08 16:52:13 +02:00
public :
2018-10-28 14:59:39 +01:00
texture_cache ( ) : m_storage ( this ) , m_predictor ( this ) { }
2019-06-08 09:33:48 +02:00
~ texture_cache ( ) = default ;
2017-11-02 07:29:17 +01:00
2018-09-22 02:14:26 +02:00
void clear ( )
{
2022-02-10 20:01:40 +01:00
// Release objects used for frame data
on_frame_end ( ) ;
// Nuke the permanent storage pool
2018-09-22 02:14:26 +02:00
m_storage . clear ( ) ;
2018-10-28 14:59:39 +01:00
m_predictor . clear ( ) ;
}
virtual void on_frame_end ( )
{
2022-02-05 19:11:56 +01:00
// Must manually release each cached entry
for ( auto & entry : m_temporary_subresource_cache )
{
release_temporary_subresource ( entry . second . second ) ;
}
2018-10-28 14:59:39 +01:00
m_temporary_subresource_cache . clear ( ) ;
m_predictor . on_frame_end ( ) ;
reset_frame_statistics ( ) ;
2018-09-22 02:14:26 +02:00
}
2019-02-25 16:03:14 +01:00
template < bool check_unlocked = false >
2022-01-09 21:07:18 +01:00
std : : vector < section_storage_type * > find_texture_from_range ( const address_range & test_range , u32 required_pitch = 0 , u32 context_mask = 0xFF )
2017-09-08 16:52:13 +02:00
{
2017-09-18 19:22:34 +02:00
std : : vector < section_storage_type * > results ;
2018-09-22 02:14:26 +02:00
2021-01-25 17:31:00 +01:00
for ( auto It = m_storage . range_begin ( test_range , full_range , check_unlocked ) ; It ! = m_storage . range_end ( ) ; It + + )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
auto & tex = * It ;
2019-02-25 16:03:14 +01:00
2021-01-25 17:31:00 +01:00
if ( ! tex . is_dirty ( ) & & ( context_mask & static_cast < u32 > ( tex . get_context ( ) ) ) )
{
2021-05-22 20:46:10 +02:00
if ( required_pitch & & ! rsx : : pitch_compatible < false > ( & tex , required_pitch , - 1 ) )
2019-02-25 16:03:14 +01:00
{
continue ;
}
2021-01-28 19:53:49 +01:00
if ( ! tex . sync_protection ( ) )
{
continue ;
}
2018-09-22 02:14:26 +02:00
results . push_back ( & tex ) ;
2019-02-02 20:44:18 +01:00
}
2017-09-08 16:52:13 +02:00
}
2017-09-18 19:22:34 +02:00
return results ;
2017-09-08 16:52:13 +02:00
}
2019-03-15 16:25:25 +01:00
template < bool check_unlocked = false >
2019-03-26 18:59:41 +01:00
section_storage_type * find_texture_from_dimensions ( u32 rsx_address , u32 format , u16 width = 0 , u16 height = 0 , u16 depth = 0 , u16 mipmaps = 0 )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
auto & block = m_storage . block_for ( rsx_address ) ;
for ( auto & tex : block )
2017-09-08 16:52:13 +02:00
{
2019-03-15 16:25:25 +01:00
if constexpr ( check_unlocked )
{
if ( ! tex . is_locked ( ) )
2021-01-28 19:53:49 +01:00
{
2019-03-15 16:25:25 +01:00
continue ;
2021-01-28 19:53:49 +01:00
}
2019-03-15 16:25:25 +01:00
}
2021-01-28 19:53:49 +01:00
if ( ! tex . is_dirty ( ) & &
tex . matches ( rsx_address , format , width , height , depth , mipmaps ) & &
tex . sync_protection ( ) )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
return & tex ;
2017-09-08 16:52:13 +02:00
}
}
return nullptr ;
}
2021-01-26 21:46:32 +01:00
section_storage_type * find_cached_texture ( const address_range & range , const image_section_attributes_t & attr , bool create_if_not_found , bool confirm_dimensions , bool allow_dirty )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
auto & block = m_storage . block_for ( range ) ;
2017-09-14 13:37:14 +02:00
2018-10-28 14:59:39 +01:00
section_storage_type * dimensions_mismatch = nullptr ;
2018-09-22 02:14:26 +02:00
section_storage_type * best_fit = nullptr ;
2018-10-19 00:22:00 +02:00
section_storage_type * reuse = nullptr ;
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
section_storage_type * res = nullptr ;
# endif
2017-09-08 16:52:13 +02:00
2018-09-22 02:14:26 +02:00
// Try to find match in block
for ( auto & tex : block )
{
if ( tex . matches ( range ) )
2017-09-08 16:52:13 +02:00
{
2021-01-28 19:53:49 +01:00
// We must validate
tex . sync_protection ( ) ;
2021-01-26 21:46:32 +01:00
if ( allow_dirty | | ! tex . is_dirty ( ) )
2017-09-08 16:52:13 +02:00
{
2021-01-26 21:46:32 +01:00
if ( ! confirm_dimensions | | tex . matches ( attr . gcm_format , attr . width , attr . height , attr . depth , attr . mipmaps ) )
2017-09-19 14:46:16 +02:00
{
2018-09-22 02:14:26 +02:00
# ifndef TEXTURE_CACHE_DEBUG
return & tex ;
# else
2020-12-09 16:04:52 +01:00
ensure ( res = = nullptr ) ;
2018-09-22 02:14:26 +02:00
res = & tex ;
# endif
2017-09-19 14:46:16 +02:00
}
2018-10-28 14:59:39 +01:00
else if ( dimensions_mismatch = = nullptr )
{
dimensions_mismatch = & tex ;
}
2017-09-08 16:52:13 +02:00
}
2018-09-22 02:14:26 +02:00
else if ( best_fit = = nullptr & & tex . can_be_reused ( ) )
{
2021-10-26 22:43:27 +02:00
// By grabbing a ref to a matching entry, duplicates are avoided
2018-09-22 02:14:26 +02:00
best_fit = & tex ;
}
2017-09-14 13:37:14 +02:00
}
2018-10-19 00:22:00 +02:00
else if ( reuse = = nullptr & & tex . can_be_reused ( ) )
2017-11-24 21:57:14 +01:00
{
2018-10-19 00:22:00 +02:00
reuse = & tex ;
2018-09-22 02:14:26 +02:00
}
}
2017-11-28 11:53:09 +01:00
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
if ( res ! = nullptr )
return res ;
# endif
2017-11-24 21:57:14 +01:00
2018-10-28 14:59:39 +01:00
if ( dimensions_mismatch ! = nullptr )
2018-09-22 02:14:26 +02:00
{
2018-10-28 14:59:39 +01:00
auto & tex = * dimensions_mismatch ;
2020-02-01 09:07:25 +01:00
rsx_log . warning ( " Cached object for address 0x%X was found, but it does not match stored parameters (width=%d vs %d; height=%d vs %d; depth=%d vs %d; mipmaps=%d vs %d) " ,
2021-01-26 21:46:32 +01:00
range . start , attr . width , tex . get_width ( ) , attr . height , tex . get_height ( ) , attr . depth , tex . get_depth ( ) , attr . mipmaps , tex . get_mipmaps ( ) ) ;
2018-09-22 02:14:26 +02:00
}
2017-11-24 21:57:14 +01:00
2018-09-22 02:14:26 +02:00
if ( ! create_if_not_found )
return nullptr ;
2017-11-28 11:53:09 +01:00
2018-09-22 02:14:26 +02:00
// If found, use the best fitting section
2018-10-19 00:22:00 +02:00
if ( best_fit ! = nullptr )
2018-09-22 02:14:26 +02:00
{
2018-10-19 00:22:00 +02:00
if ( best_fit - > exists ( ) )
{
best_fit - > destroy ( ) ;
}
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
return best_fit ;
}
// Return the first dirty section found, if any
2018-10-19 00:22:00 +02:00
if ( reuse ! = nullptr )
2018-09-22 02:14:26 +02:00
{
2018-10-19 00:22:00 +02:00
if ( reuse - > exists ( ) )
{
reuse - > destroy ( ) ;
}
2018-09-22 02:14:26 +02:00
2018-10-19 00:22:00 +02:00
return reuse ;
2017-09-08 16:52:13 +02:00
}
2018-09-22 02:14:26 +02:00
// Create and return a new section
2018-04-16 00:59:45 +02:00
update_cache_tag ( ) ;
2018-09-22 02:14:26 +02:00
auto tex = & block . create_section ( ) ;
return tex ;
2017-09-08 16:52:13 +02:00
}
2018-09-22 02:14:26 +02:00
section_storage_type * find_flushable_section ( const address_range & memory_range )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
auto & block = m_storage . block_for ( memory_range ) ;
for ( auto & tex : block )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
if ( tex . is_dirty ( ) ) continue ;
if ( ! tex . is_flushable ( ) & & ! tex . is_flushed ( ) ) continue ;
2017-09-08 16:52:13 +02:00
2018-09-22 02:14:26 +02:00
if ( tex . matches ( memory_range ) )
return & tex ;
2017-09-08 16:52:13 +02:00
}
return nullptr ;
}
2018-10-19 00:22:00 +02:00
template < typename . . . FlushArgs , typename . . . Args >
2022-01-09 21:07:18 +01:00
void lock_memory_region ( commandbuffer_type & cmd , image_storage_type * image , const address_range & rsx_range , bool is_active_surface , u16 width , u16 height , u32 pitch , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
2018-11-01 02:31:12 +01:00
AUDIT ( g_cfg . video . write_color_buffers | | g_cfg . video . write_depth_buffer ) ; // this method is only called when either WCB or WDB are enabled
2018-09-22 02:14:26 +02:00
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2017-09-08 16:52:13 +02:00
2018-10-19 00:22:00 +02:00
// Find a cached section to use
2021-01-26 21:46:32 +01:00
image_section_attributes_t search_desc = { . gcm_format = RSX_GCM_FORMAT_IGNORED , . width = width , . height = height } ;
section_storage_type & region = * find_cached_texture ( rsx_range , search_desc , true , true , false ) ;
2018-10-19 00:22:00 +02:00
// Prepare and initialize fbo region
if ( region . exists ( ) & & region . get_context ( ) ! = texture_upload_context : : framebuffer_storage )
2017-11-28 11:53:09 +01:00
{
//This space was being used for other purposes other than framebuffer storage
//Delete used resources before attaching it to framebuffer memory
2018-01-31 17:11:03 +01:00
read_only_tex_invalidate = true ;
2018-11-01 02:31:12 +01:00
}
2018-09-22 02:14:26 +02:00
2018-11-01 02:31:12 +01:00
if ( ! region . is_locked ( ) | | region . get_context ( ) ! = texture_upload_context : : framebuffer_storage )
{
// Invalidate sections from surface cache occupying same address range
2019-03-16 10:14:11 +01:00
invalidate_range_impl_base ( cmd , rsx_range , invalidation_cause : : superseded_by_fbo ) ;
2017-11-28 11:53:09 +01:00
}
2018-10-19 00:22:00 +02:00
if ( ! region . is_locked ( ) | | region . can_be_reused ( ) )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
// New region, we must prepare it
region . reset ( rsx_range ) ;
no_access_range = region . get_min_max ( no_access_range , rsx : : section_bounds : : locked_range ) ;
region . set_context ( texture_upload_context : : framebuffer_storage ) ;
region . set_image_type ( rsx : : texture_dimension_extended : : texture_dimension_2d ) ;
}
else
{
2018-10-19 00:22:00 +02:00
// Re-using clean fbo region
2020-12-09 16:04:52 +01:00
ensure ( region . matches ( rsx_range ) ) ;
ensure ( region . get_context ( ) = = texture_upload_context : : framebuffer_storage ) ;
ensure ( region . get_image_type ( ) = = rsx : : texture_dimension_extended : : texture_dimension_2d ) ;
2017-09-08 16:52:13 +02:00
}
2018-07-17 18:42:51 +02:00
region . create ( width , height , 1 , 1 , image , pitch , false , std : : forward < Args > ( extras ) . . . ) ;
2018-11-01 02:31:12 +01:00
region . reprotect ( utils : : protection : : no , { 0 , rsx_range . length ( ) } ) ;
2018-09-22 02:14:26 +02:00
region . set_dirty ( false ) ;
2018-09-19 00:21:57 +02:00
region . touch ( m_cache_update_tag ) ;
2018-04-16 00:59:45 +02:00
2019-05-15 14:55:14 +02:00
if ( is_active_surface )
2018-09-22 02:14:26 +02:00
{
2019-05-15 14:55:14 +02:00
// Add to flush always cache
if ( region . get_memory_read_flags ( ) ! = memory_read_flags : : flush_always )
{
region . set_memory_read_flags ( memory_read_flags : : flush_always , false ) ;
update_flush_always_cache ( region , true ) ;
}
else
{
AUDIT ( m_flush_always_cache . find ( region . get_section_range ( ) ) ! = m_flush_always_cache . end ( ) ) ;
}
2018-09-22 02:14:26 +02:00
}
2018-04-16 00:59:45 +02:00
update_cache_tag ( ) ;
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// Check that the cache makes sense
tex_cache_checker . verify ( ) ;
# endif // TEXTURE_CACHE_DEBUG
2018-02-03 09:37:42 +01:00
}
2019-02-25 16:03:14 +01:00
template < typename . . . Args >
void commit_framebuffer_memory_region ( commandbuffer_type & cmd , const address_range & rsx_range , Args & & . . . extras )
{
2020-02-09 14:04:22 +01:00
AUDIT ( ! g_cfg . video . write_color_buffers | | ! g_cfg . video . write_depth_buffer ) ;
2019-02-25 16:03:14 +01:00
if ( ! region_intersects_cache ( rsx_range , true ) )
return ;
std : : lock_guard lock ( m_cache_mutex ) ;
2019-02-27 19:26:22 +01:00
invalidate_range_impl_base ( cmd , rsx_range , invalidation_cause : : committed_as_fbo , std : : forward < Args > ( extras ) . . . ) ;
2019-02-25 16:03:14 +01:00
}
2020-07-15 19:45:09 +02:00
template < typename . . . Args >
2021-03-05 20:05:37 +01:00
void discard_framebuffer_memory_region ( commandbuffer_type & /*cmd*/ , const address_range & rsx_range , Args & & . . . /*extras*/ )
2020-07-15 19:45:09 +02:00
{
if ( g_cfg . video . write_color_buffers | | g_cfg . video . write_depth_buffer )
{
2021-01-26 21:46:32 +01:00
auto * region_ptr = find_cached_texture ( rsx_range , { . gcm_format = RSX_GCM_FORMAT_IGNORED } , false , false , false ) ;
2020-07-15 19:45:09 +02:00
if ( region_ptr & & region_ptr - > is_locked ( ) & & region_ptr - > get_context ( ) = = texture_upload_context : : framebuffer_storage )
{
2020-12-09 08:47:45 +01:00
ensure ( region_ptr - > get_protection ( ) = = utils : : protection : : no ) ;
2020-07-15 19:45:09 +02:00
region_ptr - > discard ( false ) ;
}
}
}
2018-09-22 02:14:26 +02:00
void set_memory_read_flags ( const address_range & memory_range , memory_read_flags flags )
2018-02-03 09:37:42 +01:00
{
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2018-02-03 09:37:42 +01:00
2022-03-27 21:38:27 +02:00
auto * region_ptr = find_cached_texture ( memory_range , { . gcm_format = RSX_GCM_FORMAT_IGNORED } , false , false , true ) ;
2018-09-22 02:14:26 +02:00
if ( region_ptr = = nullptr )
{
2018-10-19 00:22:00 +02:00
AUDIT ( m_flush_always_cache . find ( memory_range ) = = m_flush_always_cache . end ( ) ) ;
2020-02-01 09:07:25 +01:00
rsx_log . error ( " set_memory_flags(0x%x, 0x%x, %d): region_ptr == nullptr " , memory_range . start , memory_range . end , static_cast < u32 > ( flags ) ) ;
2018-09-22 02:14:26 +02:00
return ;
}
2018-02-03 09:37:42 +01:00
2022-03-27 21:38:27 +02:00
if ( region_ptr - > is_dirty ( ) )
{
// Previously invalidated
return ;
}
2018-09-22 02:14:26 +02:00
auto & region = * region_ptr ;
2018-02-03 09:37:42 +01:00
2018-10-19 00:22:00 +02:00
if ( ! region . exists ( ) | | region . is_dirty ( ) | | region . get_context ( ) ! = texture_upload_context : : framebuffer_storage )
2018-09-22 02:14:26 +02:00
{
# ifdef TEXTURE_CACHE_DEBUG
if ( ! region . is_dirty ( ) )
{
if ( flags = = memory_read_flags : : flush_once )
2020-12-09 08:47:45 +01:00
ensure ( m_flush_always_cache . find ( memory_range ) = = m_flush_always_cache . end ( ) ) ;
2018-09-22 02:14:26 +02:00
else
2020-12-09 08:47:45 +01:00
ensure ( m_flush_always_cache [ memory_range ] = = & region ) ;
2018-09-22 02:14:26 +02:00
}
# endif // TEXTURE_CACHE_DEBUG
2018-02-03 09:37:42 +01:00
return ;
2018-09-22 02:14:26 +02:00
}
2018-02-03 09:37:42 +01:00
2018-09-22 02:14:26 +02:00
update_flush_always_cache ( region , flags = = memory_read_flags : : flush_always ) ;
region . set_memory_read_flags ( flags , false ) ;
}
virtual void on_memory_read_flags_changed ( section_storage_type & section , rsx : : memory_read_flags flags )
{
# ifdef TEXTURE_CACHE_DEBUG
const auto & memory_range = section . get_section_range ( ) ;
if ( flags = = memory_read_flags : : flush_once )
2020-12-09 08:47:45 +01:00
ensure ( m_flush_always_cache [ memory_range ] = = & section ) ;
2018-09-22 02:14:26 +02:00
else
2020-12-09 08:47:45 +01:00
ensure ( m_flush_always_cache . find ( memory_range ) = = m_flush_always_cache . end ( ) ) ;
2018-09-22 02:14:26 +02:00
# endif
update_flush_always_cache ( section , flags = = memory_read_flags : : flush_always ) ;
}
2018-02-03 09:37:42 +01:00
2018-09-22 02:14:26 +02:00
private :
inline void update_flush_always_cache ( section_storage_type & section , bool add )
{
const address_range & range = section . get_section_range ( ) ;
if ( add )
{
// Add to m_flush_always_cache
AUDIT ( m_flush_always_cache . find ( range ) = = m_flush_always_cache . end ( ) ) ;
m_flush_always_cache [ range ] = & section ;
}
else
{
// Remove from m_flush_always_cache
AUDIT ( m_flush_always_cache [ range ] = = & section ) ;
m_flush_always_cache . erase ( range ) ;
}
2017-09-08 16:52:13 +02:00
}
2018-09-22 02:14:26 +02:00
public :
2017-09-08 16:52:13 +02:00
template < typename . . . Args >
2018-12-29 14:28:12 +01:00
thrashed_set invalidate_address ( commandbuffer_type & cmd , u32 address , invalidation_cause cause , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
2017-10-28 21:17:27 +02:00
//Test before trying to acquire the lock
2018-09-22 02:14:26 +02:00
const auto range = page_for ( address ) ;
2018-10-19 00:22:00 +02:00
if ( ! region_intersects_cache ( range , ! cause . is_read ( ) ) )
2017-10-28 21:17:27 +02:00
return { } ;
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2018-12-29 14:28:12 +01:00
return invalidate_range_impl_base ( cmd , range , cause , std : : forward < Args > ( extras ) . . . ) ;
2017-09-08 16:52:13 +02:00
}
2017-10-21 23:12:32 +02:00
template < typename . . . Args >
2018-12-29 14:28:12 +01:00
thrashed_set invalidate_range ( commandbuffer_type & cmd , const address_range & range , invalidation_cause cause , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
2017-10-28 10:45:43 +02:00
//Test before trying to acquire the lock
2018-10-19 00:22:00 +02:00
if ( ! region_intersects_cache ( range , ! cause . is_read ( ) ) )
2017-10-28 10:45:43 +02:00
return { } ;
2017-09-08 16:52:13 +02:00
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2018-12-29 14:28:12 +01:00
return invalidate_range_impl_base ( cmd , range , cause , std : : forward < Args > ( extras ) . . . ) ;
2017-10-21 23:12:32 +02:00
}
template < typename . . . Args >
2018-12-29 14:28:12 +01:00
bool flush_all ( commandbuffer_type & cmd , thrashed_set & data , Args & & . . . extras )
2017-10-21 23:12:32 +02:00
{
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2017-10-27 23:32:27 +02:00
2018-10-19 00:22:00 +02:00
AUDIT ( data . cause . deferred_flush ( ) ) ;
2018-09-22 02:14:26 +02:00
AUDIT ( ! data . flushed ) ;
2020-12-06 13:15:19 +01:00
if ( m_cache_update_tag . load ( ) = = data . cache_tag )
2017-10-27 23:32:27 +02:00
{
2018-05-10 13:50:32 +02:00
//1. Write memory to cpu side
2018-12-29 14:28:12 +01:00
flush_set ( cmd , data , std : : forward < Args > ( extras ) . . . ) ;
2017-12-04 17:30:09 +01:00
2018-05-10 13:50:32 +02:00
//2. Release all obsolete sections
2018-08-22 15:59:15 +02:00
unprotect_set ( data ) ;
2017-12-04 17:30:09 +01:00
}
else
{
2018-08-22 15:59:15 +02:00
// The cache contents have changed between the two readings. This means the data held is useless
2018-12-29 14:28:12 +01:00
invalidate_range_impl_base ( cmd , data . fault_range , data . cause . undefer ( ) , std : : forward < Args > ( extras ) . . . ) ;
2017-10-21 23:12:32 +02:00
}
return true ;
2017-09-08 16:52:13 +02:00
}
2018-10-28 14:59:39 +01:00
template < typename . . . Args >
2018-12-29 14:28:12 +01:00
bool flush_if_cache_miss_likely ( commandbuffer_type & cmd , const address_range & range , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
2018-10-28 14:59:39 +01:00
u32 cur_flushes_this_frame = ( m_flushes_this_frame + m_speculations_this_frame ) ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
if ( cur_flushes_this_frame > m_predict_max_flushes_per_frame )
return false ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
auto & block = m_storage . block_for ( range ) ;
if ( block . empty ( ) )
return false ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
reader_lock lock ( m_cache_mutex ) ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
// Try to find matching regions
bool result = false ;
for ( auto & region : block )
2017-09-08 16:52:13 +02:00
{
2018-10-28 14:59:39 +01:00
if ( region . is_dirty ( ) | | region . is_synchronized ( ) | | ! region . is_flushable ( ) )
continue ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
if ( ! region . matches ( range ) )
continue ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
if ( ! region . tracked_by_predictor ( ) )
continue ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
if ( ! m_predictor . predict ( region ) )
continue ;
2017-09-08 16:52:13 +02:00
2018-10-28 14:59:39 +01:00
lock . upgrade ( ) ;
2018-02-10 17:21:16 +01:00
2018-12-29 14:28:12 +01:00
region . copy_texture ( cmd , false , std : : forward < Args > ( extras ) . . . ) ;
2018-10-28 14:59:39 +01:00
result = true ;
cur_flushes_this_frame + + ;
if ( cur_flushes_this_frame > m_predict_max_flushes_per_frame )
return result ;
2017-09-08 16:52:13 +02:00
}
2018-02-10 17:21:16 +01:00
2018-10-28 14:59:39 +01:00
return result ;
2017-09-08 16:52:13 +02:00
}
2017-11-02 07:29:17 +01:00
2018-09-22 02:14:26 +02:00
void purge_unreleased_sections ( )
2017-09-08 16:52:13 +02:00
{
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
m_storage . purge_unreleased_sections ( ) ;
2017-09-08 16:52:13 +02:00
}
2021-07-14 19:09:16 +02:00
virtual bool handle_memory_pressure ( problem_severity severity )
2020-07-23 22:13:51 +02:00
{
if ( m_storage . m_unreleased_texture_objects )
{
m_storage . purge_unreleased_sections ( ) ;
return true ;
}
if ( severity > = problem_severity : : severe )
{
// Things are bad, previous check should have released 'unreleased' pool
return m_storage . purge_unlocked_sections ( ) ;
}
return false ;
}
2021-01-25 17:31:00 +01:00
void trim_sections ( )
{
std : : lock_guard lock ( m_cache_mutex ) ;
m_storage . trim_sections ( ) ;
}
2021-07-14 00:34:00 +02:00
bool evict_unused ( const std : : set < u32 > & exclusion_list )
{
2021-07-23 19:15:07 +02:00
// Some sanity checks. Do not evict if the cache is currently in use.
2021-07-14 00:34:00 +02:00
ensure ( rsx : : get_current_renderer ( ) - > is_current_thread ( ) ) ;
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 evict the texture cache because we're faulting from within in the texture cache! " ) ;
return false ;
}
2021-07-14 00:34:00 +02:00
rsx_log . warning ( " [PERFORMANCE WARNING] Texture cache is running eviction routine. This will affect performance. " ) ;
thrashed_set evicted_set ;
const u32 type_to_evict = rsx : : texture_upload_context : : shader_read | rsx : : texture_upload_context : : blit_engine_src ;
for ( auto It = m_storage . begin ( ) ; It ! = m_storage . end ( ) ; + + It )
{
auto & block = * It ;
if ( block . empty ( ) )
{
continue ;
}
for ( auto & region : block )
{
if ( region . is_dirty ( ) | | ! ( region . get_context ( ) & type_to_evict ) )
{
continue ;
}
ensure ( region . is_locked ( ) ) ;
const u32 this_address = region . get_section_base ( ) ;
if ( exclusion_list . contains ( this_address ) )
{
continue ;
}
evicted_set . violation_handled = true ;
region . set_dirty ( true ) ;
if ( region . is_locked ( true ) )
{
evicted_set . sections_to_unprotect . push_back ( & region ) ;
}
else
{
region . discard ( true ) ;
}
}
}
unprotect_set ( evicted_set ) ;
return evicted_set . violation_handled ;
}
2017-11-03 12:16:55 +01:00
image_view_type create_temporary_subresource ( commandbuffer_type & cmd , deferred_subresource & desc )
{
2020-02-05 08:00:08 +01:00
if ( ! desc . do_not_cache ) [ [ likely ] ]
2019-04-01 17:45:19 +02:00
{
2019-10-08 17:07:36 +02:00
const auto found = m_temporary_subresource_cache . equal_range ( desc . address ) ;
2019-04-01 17:45:19 +02:00
for ( auto It = found . first ; It ! = found . second ; + + It )
{
const auto & found_desc = It - > second . first ;
if ( found_desc . external_handle ! = desc . external_handle | |
found_desc . op ! = desc . op | |
found_desc . x ! = desc . x | | found_desc . y ! = desc . y | |
found_desc . width ! = desc . width | | found_desc . height ! = desc . height )
continue ;
2017-11-03 12:16:55 +01:00
2019-04-01 17:45:19 +02:00
if ( desc . op = = deferred_request_command : : copy_image_dynamic )
update_image_contents ( cmd , It - > second . second , desc . external_handle , desc . width , desc . height ) ;
2018-02-21 11:46:23 +01:00
2019-04-01 17:45:19 +02:00
return It - > second . second ;
}
2017-11-03 12:16:55 +01:00
}
2021-07-23 19:15:07 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2017-11-03 12:16:55 +01:00
image_view_type result = 0 ;
2021-07-23 19:15:07 +02:00
2018-03-30 12:28:46 +02:00
switch ( desc . op )
{
case deferred_request_command : : cubemap_gather :
{
result = generate_cubemap_from_images ( cmd , desc . gcm_format , desc . width , desc . sections_to_copy , desc . remap ) ;
break ;
}
case deferred_request_command : : cubemap_unwrap :
{
std : : vector < copy_region_descriptor > sections ( 6 ) ;
for ( u16 n = 0 ; n < 6 ; + + n )
{
2019-02-02 20:44:18 +01:00
sections [ n ] =
{
desc . external_handle ,
2019-10-02 18:27:48 +02:00
surface_transform : : coordinate_transform ,
2019-10-09 21:29:23 +02:00
0 ,
2019-12-03 23:34:23 +01:00
0 , static_cast < u16 > ( desc . slice_h * n ) ,
2019-02-02 20:44:18 +01:00
0 , 0 , n ,
desc . width , desc . height ,
desc . width , desc . height
} ;
2018-03-30 12:28:46 +02:00
}
result = generate_cubemap_from_images ( cmd , desc . gcm_format , desc . width , sections , desc . remap ) ;
break ;
}
case deferred_request_command : : _3d_gather :
{
result = generate_3d_from_2d_images ( cmd , desc . gcm_format , desc . width , desc . height , desc . depth , desc . sections_to_copy , desc . remap ) ;
break ;
}
case deferred_request_command : : _3d_unwrap :
{
std : : vector < copy_region_descriptor > sections ;
sections . resize ( desc . depth ) ;
for ( u16 n = 0 ; n < desc . depth ; + + n )
{
2019-02-02 20:44:18 +01:00
sections [ n ] =
{
desc . external_handle ,
2019-10-02 18:27:48 +02:00
surface_transform : : coordinate_transform ,
2019-10-09 21:29:23 +02:00
0 ,
2019-12-03 23:34:23 +01:00
0 , static_cast < u16 > ( desc . slice_h * n ) ,
2019-02-02 20:44:18 +01:00
0 , 0 , n ,
desc . width , desc . height ,
desc . width , desc . height
} ;
2018-03-30 12:28:46 +02:00
}
result = generate_3d_from_2d_images ( cmd , desc . gcm_format , desc . width , desc . height , desc . depth , sections , desc . remap ) ;
break ;
}
case deferred_request_command : : atlas_gather :
{
2018-03-18 12:40:26 +01:00
result = generate_atlas_from_images ( cmd , desc . gcm_format , desc . width , desc . height , desc . sections_to_copy , desc . remap ) ;
2018-03-30 12:28:46 +02:00
break ;
}
case deferred_request_command : : copy_image_static :
case deferred_request_command : : copy_image_dynamic :
{
2018-03-18 12:40:26 +01:00
result = create_temporary_subresource_view ( cmd , & desc . external_handle , desc . gcm_format , desc . x , desc . y , desc . width , desc . height , desc . remap ) ;
2018-03-30 12:28:46 +02:00
break ;
}
2019-10-09 21:29:23 +02:00
case deferred_request_command : : mipmap_gather :
{
result = generate_2d_mipmaps_from_images ( cmd , desc . gcm_format , desc . width , desc . height , desc . sections_to_copy , desc . remap ) ;
break ;
}
2018-03-30 12:28:46 +02:00
default :
{
//Throw
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Invalid deferred command op 0x%X " , static_cast < u32 > ( desc . op ) ) ;
2018-03-30 12:28:46 +02:00
}
}
2017-11-03 12:16:55 +01:00
2020-02-05 08:00:08 +01:00
if ( result ) [ [ likely ] ]
2017-11-03 12:16:55 +01:00
{
2020-02-05 08:00:08 +01:00
if ( ! desc . do_not_cache ) [ [ likely ] ]
2019-10-17 21:52:53 +02:00
{
m_temporary_subresource_cache . insert ( { desc . address , { desc , result } } ) ;
}
else
{
m_uncached_subresources . push_back ( result ) ;
}
2017-11-03 12:16:55 +01:00
}
return result ;
}
2019-10-17 21:52:53 +02:00
void release_uncached_temporary_subresources ( )
{
for ( auto & view : m_uncached_subresources )
{
release_temporary_subresource ( view ) ;
}
m_uncached_subresources . clear ( ) ;
}
2019-04-01 17:45:19 +02:00
void notify_surface_changed ( const utils : : address_range & range )
2017-11-03 12:16:55 +01:00
{
2019-04-01 17:45:19 +02:00
for ( auto It = m_temporary_subresource_cache . begin ( ) ; It ! = m_temporary_subresource_cache . end ( ) ; )
{
2019-10-10 20:40:03 +02:00
const auto & desc = It - > second . first ;
if ( range . overlaps ( desc . cache_range ) )
2019-04-01 17:45:19 +02:00
{
2019-10-17 20:35:04 +02:00
release_temporary_subresource ( It - > second . second ) ;
2019-04-01 17:45:19 +02:00
It = m_temporary_subresource_cache . erase ( It ) ;
}
else
{
+ + It ;
}
}
2017-11-03 12:16:55 +01:00
}
2021-03-05 20:05:37 +01:00
template < typename SurfaceStoreType , typename . . . Args >
2019-10-08 17:07:36 +02:00
sampled_image_descriptor fast_texture_search (
commandbuffer_type & cmd ,
const image_section_attributes_t & attr ,
2021-07-31 16:27:16 +02:00
const size3f & scale ,
2019-10-08 17:07:36 +02:00
u32 encoded_remap ,
const texture_channel_remap_t & remap ,
2019-10-10 17:32:54 +02:00
const texture_cache_search_options & options ,
2019-10-08 17:07:36 +02:00
const utils : : address_range & memory_range ,
2019-03-11 21:18:22 +01:00
rsx : : texture_dimension_extended extended_dimension ,
2021-03-05 20:05:37 +01:00
SurfaceStoreType & m_rtts , Args & & . . . /*extras*/ )
2019-02-25 16:03:14 +01:00
{
2020-02-05 08:00:08 +01:00
if ( options . is_compressed_format ) [ [ likely ] ]
2019-03-16 12:31:39 +01:00
{
2019-05-11 17:31:49 +02:00
// Most mesh textures are stored as compressed to make the most of the limited memory
2019-10-08 17:07:36 +02:00
if ( auto cached_texture = find_texture_from_dimensions ( attr . address , attr . gcm_format , attr . width , attr . height , attr . depth ) )
2019-03-16 12:31:39 +01:00
{
2020-08-15 23:33:34 +02:00
return { cached_texture - > get_view ( encoded_remap , remap ) , cached_texture - > get_context ( ) , cached_texture - > get_format_class ( ) , scale , cached_texture - > get_image_type ( ) } ;
2019-03-16 12:31:39 +01:00
}
}
2019-05-11 17:31:49 +02:00
else
{
2019-10-08 17:07:36 +02:00
// Fast lookup for cyclic reference
2020-02-05 08:00:08 +01:00
if ( m_rtts . address_is_bound ( attr . address ) ) [ [ unlikely ] ]
2019-05-11 17:31:49 +02:00
{
2019-10-08 17:07:36 +02:00
if ( auto texptr = m_rtts . get_surface_at ( attr . address ) ;
2019-11-17 21:03:18 +01:00
helpers : : check_framebuffer_resource ( texptr , attr , extended_dimension ) )
2019-05-11 17:31:49 +02:00
{
2019-10-08 17:07:36 +02:00
const bool force_convert = ! render_target_format_is_compatible ( texptr , attr . gcm_format ) ;
2019-11-17 21:03:18 +01:00
auto result = helpers : : process_framebuffer_resource_fast < sampled_image_descriptor > (
2019-10-08 17:07:36 +02:00
cmd , texptr , attr , scale , extended_dimension , encoded_remap , remap , true , force_convert ) ;
2019-10-17 22:30:49 +02:00
if ( ! options . skip_texture_barriers & & result . is_cyclic_reference )
2019-10-09 21:29:23 +02:00
{
2019-10-17 22:30:49 +02:00
// A texture barrier is only necessary when the rendertarget is going to be bound as a shader input.
// If a temporary copy is to be made, this should not be invoked
2019-10-09 21:29:23 +02:00
insert_texture_barrier ( cmd , texptr ) ;
}
2019-10-17 22:30:49 +02:00
2019-10-08 17:07:36 +02:00
return result ;
2019-05-11 17:31:49 +02:00
}
}
2019-03-16 12:31:39 +01:00
2021-03-05 20:05:37 +01:00
std : : vector < typename SurfaceStoreType : : surface_overlap_info > overlapping_fbos ;
2019-10-10 17:32:54 +02:00
std : : vector < section_storage_type * > overlapping_locals ;
auto fast_fbo_check = [ & ] ( ) - > sampled_image_descriptor
{
const auto & last = overlapping_fbos . back ( ) ;
if ( last . src_area . x = = 0 & & last . src_area . y = = 0 & & ! last . is_clipped )
{
const bool force_convert = ! render_target_format_is_compatible ( last . surface , attr . gcm_format ) ;
2019-11-17 21:03:18 +01:00
return helpers : : process_framebuffer_resource_fast < sampled_image_descriptor > (
2019-10-10 17:32:54 +02:00
cmd , last . surface , attr , scale , extended_dimension , encoded_remap , remap , false , force_convert ) ;
}
return { } ;
} ;
// Check surface cache early if the option is enabled
if ( options . prefer_surface_cache )
{
2019-10-12 13:11:15 +02:00
const u16 block_h = ( attr . depth * attr . slice_h ) ;
2021-05-08 18:08:32 +02:00
overlapping_fbos = m_rtts . get_merged_texture_memory_region ( cmd , attr . address , attr . width , block_h , attr . pitch , attr . bpp , rsx : : surface_access : : shader_read ) ;
2019-10-10 17:32:54 +02:00
if ( ! overlapping_fbos . empty ( ) )
{
if ( auto result = fast_fbo_check ( ) ; result . validate ( ) )
{
return result ;
}
if ( options . skip_texture_merge )
{
overlapping_fbos . clear ( ) ;
}
}
}
2019-10-08 17:07:36 +02:00
// Check shader_read storage. In a given scene, reads from local memory far outnumber reads from the surface cache
const u32 lookup_mask = rsx : : texture_upload_context : : shader_read | rsx : : texture_upload_context : : blit_engine_dst | rsx : : texture_upload_context : : blit_engine_src ;
2019-10-10 17:32:54 +02:00
overlapping_locals = find_texture_from_range < true > ( memory_range , attr . height > 1 ? attr . pitch : 0 , lookup_mask & options . lookup_mask ) ;
2019-09-01 16:20:52 +02:00
// Search for exact match if possible
2019-05-11 17:31:49 +02:00
for ( auto & cached_texture : overlapping_locals )
2019-02-25 16:03:14 +01:00
{
2019-10-08 17:07:36 +02:00
if ( cached_texture - > matches ( attr . address , attr . gcm_format , attr . width , attr . height , attr . depth , 0 ) )
2019-05-11 17:31:49 +02:00
{
2020-01-23 20:35:03 +01:00
# ifdef TEXTURE_CACHE_DEBUG
if ( ! memory_range . inside ( cached_texture - > get_confirmed_range ( ) ) )
{
// TODO. This is easily possible for blit_dst textures if the blit is incomplete in Y
// The possibility that a texture will be split into parts on the CPU like this is very rare
continue ;
}
# endif
2020-08-01 13:27:13 +02:00
if ( attr . swizzled ! = cached_texture - > is_swizzled ( ) )
{
// We can have the correct data in cached_texture but it needs decoding before it can be sampled.
// Usually a sign of a game bug where the developer forgot to mark the texture correctly the first time we see it.
2020-09-18 14:23:51 +02:00
// TODO: This section should execute under an exclusive lock, but we're not actually modifying any object references, only flags
2020-09-18 20:06:07 +02:00
rsx_log . warning ( " A texture was found in cache for address 0x%x, but swizzle flag does not match " , attr . address ) ;
2020-09-18 14:23:51 +02:00
cached_texture - > unprotect ( ) ;
cached_texture - > set_dirty ( true ) ;
return { } ;
2020-08-01 13:27:13 +02:00
}
2020-08-15 23:33:34 +02:00
return { cached_texture - > get_view ( encoded_remap , remap ) , cached_texture - > get_context ( ) , cached_texture - > get_format_class ( ) , scale , cached_texture - > get_image_type ( ) } ;
2019-05-11 17:31:49 +02:00
}
2017-10-23 17:17:54 +02:00
}
2019-09-01 16:20:52 +02:00
if ( ! overlapping_locals . empty ( ) )
{
2019-10-08 17:07:36 +02:00
// Remove everything that is not a transfer target
overlapping_locals . erase
(
std : : remove_if ( overlapping_locals . begin ( ) , overlapping_locals . end ( ) , [ ] ( const auto & e )
{
return ( e - > get_context ( ) ! = rsx : : texture_upload_context : : blit_engine_dst ) ;
} ) ,
overlapping_locals . end ( )
) ;
2019-09-01 16:20:52 +02:00
}
2019-10-10 17:32:54 +02:00
if ( ! options . prefer_surface_cache )
{
// Now check for surface cache hits
2019-10-12 13:11:15 +02:00
const u16 block_h = ( attr . depth * attr . slice_h ) ;
2021-05-08 18:08:32 +02:00
overlapping_fbos = m_rtts . get_merged_texture_memory_region ( cmd , attr . address , attr . width , block_h , attr . pitch , attr . bpp , rsx : : surface_access : : shader_read ) ;
2019-10-10 17:32:54 +02:00
}
2019-02-25 16:03:14 +01:00
if ( ! overlapping_fbos . empty ( ) | | ! overlapping_locals . empty ( ) )
2017-09-14 13:37:14 +02:00
{
2019-02-25 16:03:14 +01:00
int _pool = - 1 ;
2020-02-05 08:00:08 +01:00
if ( overlapping_locals . empty ( ) ) [ [ likely ] ]
2017-11-24 21:57:14 +01:00
{
2019-02-25 16:03:14 +01:00
_pool = 0 ;
2017-11-24 21:57:14 +01:00
}
2019-02-25 16:03:14 +01:00
else if ( overlapping_fbos . empty ( ) )
2017-11-24 21:57:14 +01:00
{
2019-02-25 16:03:14 +01:00
_pool = 1 ;
}
else
{
_pool = ( overlapping_locals . back ( ) - > last_write_tag < overlapping_fbos . back ( ) . surface - > last_use_tag ) ? 0 : 1 ;
2017-11-24 21:57:14 +01:00
}
2017-09-18 19:22:34 +02:00
2019-02-25 16:03:14 +01:00
if ( _pool = = 0 )
2017-09-18 19:22:34 +02:00
{
2019-02-25 16:03:14 +01:00
// Surface cache data is newer, check if this thing fits our search parameters
2019-10-10 17:32:54 +02:00
if ( ! options . prefer_surface_cache )
2017-09-18 19:22:34 +02:00
{
2019-10-10 17:32:54 +02:00
if ( auto result = fast_fbo_check ( ) ; result . validate ( ) )
{
return result ;
}
2017-09-18 19:22:34 +02:00
}
}
2019-02-28 11:42:51 +01:00
else if ( extended_dimension < = rsx : : texture_dimension_extended : : texture_dimension_2d )
{
const auto last = overlapping_locals . back ( ) ;
2019-10-08 17:07:36 +02:00
const auto normalized_width = u16 ( last - > get_width ( ) * get_format_block_size_in_bytes ( last - > get_gcm_format ( ) ) ) / attr . bpp ;
2019-03-29 20:04:54 +01:00
2019-10-08 17:07:36 +02:00
if ( last - > get_section_base ( ) = = attr . address & &
normalized_width > = attr . width & & last - > get_height ( ) > = attr . height )
2019-02-28 11:42:51 +01:00
{
2019-10-08 17:07:36 +02:00
u32 gcm_format = attr . gcm_format ;
2019-11-17 21:03:18 +01:00
const bool gcm_format_is_depth = helpers : : is_gcm_depth_format ( attr . gcm_format ) ;
2019-03-29 20:04:54 +01:00
2019-10-20 13:53:02 +02:00
if ( ! gcm_format_is_depth & & last - > is_depth_texture ( ) )
2019-03-29 20:04:54 +01:00
{
2019-10-20 13:53:02 +02:00
// While the copy routines can perform a typeless cast, prefer to not cross the aspect barrier if possible
2019-11-17 21:03:18 +01:00
gcm_format = helpers : : get_compatible_depth_format ( attr . gcm_format ) ;
2019-03-29 20:04:54 +01:00
}
2019-10-08 17:07:36 +02:00
auto new_attr = attr ;
new_attr . gcm_format = gcm_format ;
return { last - > get_raw_texture ( ) , deferred_request_command : : copy_image_static , new_attr , { } ,
2020-08-15 23:33:34 +02:00
last - > get_context ( ) , classify_format ( gcm_format ) , scale , extended_dimension , remap } ;
2019-02-28 11:42:51 +01:00
}
}
2019-02-25 16:03:14 +01:00
2019-11-17 21:03:18 +01:00
auto result = helpers : : merge_cache_resources < sampled_image_descriptor > (
2022-03-13 09:32:04 +01:00
cmd , overlapping_fbos , overlapping_locals , attr , scale , extended_dimension , encoded_remap , remap , _pool ) ;
2019-10-12 15:36:41 +02:00
2019-10-10 17:32:54 +02:00
if ( options . skip_texture_merge )
{
2019-10-12 15:36:41 +02:00
switch ( result . external_subresource_desc . op )
{
case deferred_request_command : : copy_image_static :
case deferred_request_command : : copy_image_dynamic :
return result ;
default :
break ;
}
2019-10-10 17:32:54 +02:00
return { } ;
}
2020-01-23 21:20:22 +01:00
if ( const auto section_count = result . external_subresource_desc . sections_to_copy . size ( ) ;
2020-01-25 12:39:46 +01:00
section_count > 0 )
2019-02-28 11:42:51 +01:00
{
2022-01-22 12:56:45 +01:00
bool result_is_valid ;
if ( _pool = = 0 & & ! g_cfg . video . write_color_buffers & & ! g_cfg . video . write_depth_buffer )
2020-02-03 17:06:25 +01:00
{
// HACK: Avoid WCB requirement for some games with wrongly declared sampler dimensions.
// TODO: Some games may render a small region (e.g 1024x256x2) and sample a huge texture (e.g 1024x1024).
// Seen in APF2k8 - this causes missing bits to be reuploaded from CPU which can cause WCB requirement.
// Properly fix this by introducing partial data upload into the surface cache in such cases and making RCB/RDB
// enabled by default. Blit engine already handles this correctly.
result_is_valid = true ;
}
2022-01-22 12:56:45 +01:00
else
{
result_is_valid = result . atlas_covers_target_area ( section_count = = 1 ? 99 : 90 ) ;
}
2019-04-01 17:45:19 +02:00
2020-02-03 17:06:25 +01:00
if ( result_is_valid )
2019-04-01 17:45:19 +02:00
{
2021-02-13 15:35:36 +01:00
// Check for possible duplicates
2022-01-06 20:34:28 +01:00
usz max_overdraw_ratio = u32 { umax } ;
usz max_safe_sections = u32 { umax } ;
2021-02-13 15:35:36 +01:00
switch ( result . external_subresource_desc . op )
{
case deferred_request_command : : atlas_gather :
2022-01-06 20:34:28 +01:00
max_overdraw_ratio = 150 ;
max_safe_sections = 8 + 2 * attr . mipmaps ;
break ;
2021-02-13 15:35:36 +01:00
case deferred_request_command : : cubemap_gather :
2022-01-06 20:34:28 +01:00
max_overdraw_ratio = 150 ;
max_safe_sections = 6 * 2 * attr . mipmaps ;
break ;
2021-02-13 15:35:36 +01:00
case deferred_request_command : : _3d_gather :
2022-01-06 20:34:28 +01:00
// 3D gather can have very many input sections, try to keep section count low
max_overdraw_ratio = 125 ;
max_safe_sections = ( attr . depth * attr . mipmaps * 110 ) / 100 ;
break ;
2021-02-13 15:35:36 +01:00
default :
break ;
}
2022-01-05 18:25:16 +01:00
if ( overlapping_fbos . size ( ) > max_safe_sections )
2021-02-13 15:35:36 +01:00
{
2022-01-06 20:34:28 +01:00
// Are we really over-budget?
u32 coverage_size = 0 ;
for ( const auto & section : overlapping_fbos )
{
2022-03-05 16:59:21 +01:00
const auto area = section . surface - > get_native_pitch ( ) * section . surface - > template get_surface_height < rsx : : surface_metrics : : bytes > ( ) ;
2022-01-06 20:34:28 +01:00
coverage_size + = area ;
}
if ( const auto coverage_ratio = ( coverage_size * 100ull ) / memory_range . length ( ) ;
coverage_ratio > max_overdraw_ratio )
{
2022-02-06 22:29:23 +01:00
rsx_log . warning ( " [Performance warning] Texture gather routine encountered too many objects! Operation=%d, Mipmaps=%d, Depth=%d, Sections=%zu, Ratio=%llu% " ,
2022-01-06 20:34:28 +01:00
static_cast < int > ( result . external_subresource_desc . op ) , attr . mipmaps , attr . depth , overlapping_fbos . size ( ) , coverage_ratio ) ;
2022-04-28 19:18:12 +02:00
m_rtts . check_for_duplicates ( overlapping_fbos ) ;
2022-01-06 20:34:28 +01:00
}
2021-02-13 15:35:36 +01:00
}
2020-01-25 12:39:46 +01:00
// Optionally disallow caching if resource is being written to as it is being read from
for ( const auto & section : overlapping_fbos )
2019-04-01 17:45:19 +02:00
{
2020-01-25 12:39:46 +01:00
if ( m_rtts . address_is_bound ( section . base_address ) )
2019-04-01 17:45:19 +02:00
{
2020-01-25 12:39:46 +01:00
if ( result . external_subresource_desc . op = = deferred_request_command : : copy_image_static )
{
result . external_subresource_desc . op = deferred_request_command : : copy_image_dynamic ;
}
else
{
result . external_subresource_desc . do_not_cache = true ;
}
break ;
2019-04-01 17:45:19 +02:00
}
}
2020-01-25 12:39:46 +01:00
return result ;
}
2019-02-28 11:42:51 +01:00
}
2017-09-18 19:22:34 +02:00
}
2019-02-25 16:03:14 +01:00
}
2017-09-14 13:37:14 +02:00
2019-10-08 17:07:36 +02:00
return { } ;
}
2021-06-05 19:05:12 +02:00
template < typename surface_store_type , typename RsxTextureType >
bool test_if_descriptor_expired ( commandbuffer_type & cmd , surface_store_type & surface_cache , sampled_image_descriptor * descriptor , const RsxTextureType & tex )
2020-12-14 18:24:28 +01:00
{
auto result = descriptor - > is_expired ( surface_cache ) ;
if ( result . second & & descriptor - > is_cyclic_reference )
{
/* NOTE: All cyclic descriptors updated via fast update must have a barrier check
* It is possible for the following sequence of events to break common - sense tests
* 1. Cyclic ref occurs normally in upload_texture
* 2. Surface is swappd out , but texture is not updated
* 3. Surface is swapped back in . Surface cache resets layout to optimal rasterization layout
* 4. During bind , the surface is converted to shader layout because it is not in GENERAL layout
*/
if ( ! g_cfg . video . strict_rendering_mode )
{
insert_texture_barrier ( cmd , result . second , false ) ;
}
else if ( descriptor - > image_handle )
{
2021-06-05 19:05:12 +02:00
// Rebuild duplicate surface
auto src = descriptor - > image_handle - > image ( ) ;
rsx : : image_section_attributes_t attr ;
attr . address = descriptor - > ref_address ;
attr . gcm_format = tex . format ( ) & ~ ( CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN ) ;
attr . width = src - > width ( ) ;
attr . height = src - > height ( ) ;
attr . depth = 1 ;
attr . mipmaps = 1 ;
attr . pitch = 0 ; // Unused
attr . slice_h = src - > height ( ) ;
attr . bpp = get_format_block_size_in_bytes ( attr . gcm_format ) ;
attr . swizzled = false ;
// Sanity checks
const bool gcm_format_is_depth = helpers : : is_gcm_depth_format ( attr . gcm_format ) ;
const bool bound_surface_is_depth = surface_cache . m_bound_depth_stencil . first = = attr . address ;
if ( ! gcm_format_is_depth & & bound_surface_is_depth )
{
// While the copy routines can perform a typeless cast, prefer to not cross the aspect barrier if possible
// This avoids messing with other solutions such as texture redirection as well
attr . gcm_format = helpers : : get_compatible_depth_format ( attr . gcm_format ) ;
}
descriptor - > external_subresource_desc =
{
src ,
rsx : : deferred_request_command : : copy_image_dynamic ,
attr ,
{ } ,
rsx : : default_remap_vector
} ;
2020-12-14 18:24:28 +01:00
descriptor - > external_subresource_desc . do_not_cache = true ;
2021-06-05 19:05:12 +02:00
descriptor - > image_handle = nullptr ;
2020-12-14 18:24:28 +01:00
}
else
{
// Force reupload
return true ;
}
}
return result . first ;
}
2019-10-08 17:07:36 +02:00
template < typename RsxTextureType , typename surface_store_type , typename . . . Args >
2021-06-05 19:05:12 +02:00
sampled_image_descriptor upload_texture ( commandbuffer_type & cmd , const RsxTextureType & tex , surface_store_type & m_rtts , Args & & . . . extras )
2019-10-08 17:07:36 +02:00
{
2020-12-14 19:29:07 +01:00
m_texture_upload_calls_this_frame + + ;
2019-10-08 17:07:36 +02:00
image_section_attributes_t attributes { } ;
2019-10-10 17:32:54 +02:00
texture_cache_search_options options { } ;
2020-12-09 16:04:52 +01:00
attributes . address = rsx : : get_address ( tex . offset ( ) , tex . location ( ) ) ;
2019-10-08 17:07:36 +02:00
attributes . gcm_format = tex . format ( ) & ~ ( CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN ) ;
attributes . bpp = get_format_block_size_in_bytes ( attributes . gcm_format ) ;
attributes . width = tex . width ( ) ;
attributes . height = tex . height ( ) ;
2021-02-13 15:35:36 +01:00
attributes . mipmaps = tex . get_exact_mipmap_count ( ) ;
2020-08-01 13:27:13 +02:00
attributes . swizzled = ! ( tex . format ( ) & CELL_GCM_TEXTURE_LN ) ;
2019-10-08 17:07:36 +02:00
const bool is_unnormalized = ! ! ( tex . format ( ) & CELL_GCM_TEXTURE_UN ) ;
2019-10-17 23:21:09 +02:00
auto extended_dimension = tex . get_extended_texture_dimension ( ) ;
2019-10-08 17:07:36 +02:00
2019-11-17 21:03:18 +01:00
options . is_compressed_format = helpers : : is_compressed_gcm_format ( attributes . gcm_format ) ;
2019-10-10 17:32:54 +02:00
2021-02-17 20:58:10 +01:00
u32 tex_size = 0 , required_surface_height = 1 ;
u8 subsurface_count = 1 ;
2021-07-31 16:27:16 +02:00
size3f scale { 1.f , 1.f , 1.f } ;
2017-09-08 16:52:13 +02:00
2020-07-08 22:47:40 +02:00
if ( is_unnormalized )
{
2021-08-06 23:49:50 +02:00
switch ( extended_dimension )
2020-07-08 22:47:40 +02:00
{
2021-08-06 23:49:50 +02:00
case rsx : : texture_dimension_extended : : texture_dimension_3d :
case rsx : : texture_dimension_extended : : texture_dimension_cubemap :
2021-07-31 16:27:16 +02:00
scale . depth / = attributes . depth ;
2021-08-06 23:49:50 +02:00
[ [ fallthrough ] ] ;
case rsx : : texture_dimension_extended : : texture_dimension_2d :
scale . height / = attributes . height ;
[ [ fallthrough ] ] ;
default :
scale . width / = attributes . width ;
break ;
2020-07-08 22:47:40 +02:00
}
}
2020-08-01 13:27:13 +02:00
const auto packed_pitch = get_format_packed_pitch ( attributes . gcm_format , attributes . width , ! tex . border_type ( ) , attributes . swizzled ) ;
if ( ! attributes . swizzled ) [ [ likely ] ]
2019-02-25 16:03:14 +01:00
{
2019-10-08 17:07:36 +02:00
if ( attributes . pitch = tex . pitch ( ) ; ! attributes . pitch )
{
2020-07-08 22:47:40 +02:00
attributes . pitch = packed_pitch ;
2021-10-30 13:22:56 +02:00
scale = { 1.f , 0.f , 0.f } ;
2019-10-08 17:07:36 +02:00
}
2020-07-08 22:47:40 +02:00
else if ( packed_pitch > attributes . pitch & & ! options . is_compressed_format )
{
scale . width * = f32 ( packed_pitch ) / attributes . pitch ;
attributes . width = attributes . pitch / attributes . bpp ;
}
2019-10-08 17:07:36 +02:00
}
else
{
2020-07-08 22:47:40 +02:00
attributes . pitch = packed_pitch ;
2019-10-08 17:07:36 +02:00
}
switch ( extended_dimension )
{
2022-03-01 18:20:22 +01:00
case rsx : : texture_dimension_extended : : texture_dimension_1d :
attributes . depth = 1 ;
attributes . height = 1 ;
attributes . slice_h = 1 ;
scale . height = scale . depth = 0.f ;
subsurface_count = 1 ;
required_surface_height = 1 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_2d :
attributes . depth = 1 ;
scale . depth = 0.f ;
subsurface_count = options . is_compressed_format ? 1 : tex . get_exact_mipmap_count ( ) ;
attributes . slice_h = required_surface_height = attributes . height ;
break ;
2019-10-08 17:07:36 +02:00
case rsx : : texture_dimension_extended : : texture_dimension_cubemap :
attributes . depth = 6 ;
subsurface_count = 1 ;
2019-12-03 23:34:23 +01:00
tex_size = static_cast < u32 > ( get_texture_size ( tex ) ) ;
2019-10-08 17:07:36 +02:00
required_surface_height = tex_size / attributes . pitch ;
attributes . slice_h = required_surface_height / attributes . depth ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_3d :
attributes . depth = tex . depth ( ) ;
subsurface_count = 1 ;
2022-03-01 18:20:22 +01:00
tex_size = static_cast < u32 > ( get_texture_size ( tex ) ) ;
required_surface_height = tex_size / attributes . pitch ;
attributes . slice_h = required_surface_height / attributes . depth ;
2019-02-25 16:03:14 +01:00
break ;
2021-07-31 16:27:16 +02:00
default :
fmt : : throw_exception ( " Unsupported texture dimension %d " , static_cast < int > ( extended_dimension ) ) ;
2019-02-25 16:03:14 +01:00
}
2018-09-22 02:14:26 +02:00
2022-01-28 20:42:55 +01:00
// Validation
if ( ! attributes . width | | ! attributes . height | | ! attributes . depth )
{
rsx_log . warning ( " Image at address 0x%x has invalid dimensions. Type=%d, Dims=%dx%dx%d " ,
attributes . address , static_cast < s32 > ( extended_dimension ) ,
attributes . width , attributes . height , attributes . depth ) ;
return { } ;
}
2019-10-17 23:21:09 +02:00
if ( options . is_compressed_format )
{
2019-10-25 17:32:36 +02:00
// Compressed textures cannot be 1D in some APIs
2019-10-17 23:21:09 +02:00
extended_dimension = std : : max ( extended_dimension , rsx : : texture_dimension_extended : : texture_dimension_2d ) ;
}
2019-10-08 17:07:36 +02:00
const auto lookup_range = utils : : address_range : : start_length ( attributes . address , attributes . pitch * required_surface_height ) ;
reader_lock lock ( m_cache_mutex ) ;
auto result = fast_texture_search ( cmd , attributes , scale , tex . remap ( ) , tex . decoded_remap ( ) ,
2019-10-10 17:32:54 +02:00
options , lookup_range , extended_dimension , m_rtts ,
2019-10-08 17:07:36 +02:00
std : : forward < Args > ( extras ) . . . ) ;
if ( result . validate ( ) )
{
2020-02-05 08:00:08 +01:00
if ( ! result . image_handle ) [ [ unlikely ] ]
2019-10-10 19:00:45 +02:00
{
// Deferred reconstruct
2019-10-10 20:40:03 +02:00
result . external_subresource_desc . cache_range = lookup_range ;
2019-10-10 19:00:45 +02:00
}
2020-12-14 18:24:28 +01:00
result . ref_address = attributes . address ;
2022-03-05 14:19:17 +01:00
result . surface_cache_tag = m_rtts . write_tag ;
2020-12-14 18:24:28 +01:00
2019-10-09 21:29:23 +02:00
if ( subsurface_count = = 1 )
2019-10-08 17:07:36 +02:00
{
return result ;
}
2019-10-09 21:29:23 +02:00
switch ( result . upload_context )
2019-10-08 17:07:36 +02:00
{
2019-10-09 21:29:23 +02:00
case rsx : : texture_upload_context : : blit_engine_dst :
case rsx : : texture_upload_context : : framebuffer_storage :
break ;
case rsx : : texture_upload_context : : shader_read :
if ( ! result . image_handle )
break ;
2018-09-06 13:28:12 +02:00
[ [ fallthrough ] ] ;
2019-10-09 21:29:23 +02:00
default :
2019-10-08 17:07:36 +02:00
return result ;
}
2019-10-09 21:29:23 +02:00
// Traverse the mipmap tree
// Some guarantees here include:
// 1. Only 2D images will invoke this routine
// 2. The image has to have been generated on the GPU (fbo or blit target only)
2019-10-08 17:07:36 +02:00
2019-10-09 21:29:23 +02:00
std : : vector < copy_region_descriptor > sections ;
const bool use_upscaling = ( result . upload_context = = rsx : : texture_upload_context : : framebuffer_storage & & g_cfg . video . resolution_scale_percent ! = 100 ) ;
2019-10-08 17:07:36 +02:00
2020-02-05 08:00:08 +01:00
if ( ! helpers : : append_mipmap_level ( sections , result , attributes , 0 , use_upscaling , attributes ) ) [ [ unlikely ] ]
2019-10-08 17:07:36 +02:00
{
2019-10-09 21:29:23 +02:00
// Abort if mip0 is not compatible
return result ;
}
2019-10-08 17:07:36 +02:00
2019-10-09 21:29:23 +02:00
auto attr2 = attributes ;
sections . reserve ( subsurface_count ) ;
2019-10-10 17:32:54 +02:00
options . skip_texture_merge = true ;
options . skip_texture_barriers = true ;
options . prefer_surface_cache = ( result . upload_context = = rsx : : texture_upload_context : : framebuffer_storage ) ;
2019-10-09 21:29:23 +02:00
for ( u8 subsurface = 1 ; subsurface < subsurface_count ; + + subsurface )
{
attr2 . address + = ( attr2 . pitch * attr2 . height ) ;
attr2 . width = std : : max ( attr2 . width / 2 , 1 ) ;
attr2 . height = std : : max ( attr2 . height / 2 , 1 ) ;
2019-10-12 15:36:41 +02:00
attr2 . slice_h = attr2 . height ;
2019-10-08 17:07:36 +02:00
2020-08-01 13:27:13 +02:00
if ( attributes . swizzled )
2019-10-08 17:07:36 +02:00
{
2019-10-09 21:29:23 +02:00
attr2 . pitch = attr2 . width * attr2 . bpp ;
2019-10-08 17:07:36 +02:00
}
2019-10-09 21:29:23 +02:00
const auto range = utils : : address_range : : start_length ( attr2 . address , attr2 . pitch * attr2 . height ) ;
auto ret = fast_texture_search ( cmd , attr2 , scale , tex . remap ( ) , tex . decoded_remap ( ) ,
2019-10-10 17:32:54 +02:00
options , range , extended_dimension , m_rtts , std : : forward < Args > ( extras ) . . . ) ;
2019-10-09 21:29:23 +02:00
if ( ! ret . validate ( ) | |
2019-11-17 21:03:18 +01:00
! helpers : : append_mipmap_level ( sections , ret , attr2 , subsurface , use_upscaling , attributes ) )
2019-10-08 17:07:36 +02:00
{
2019-10-09 21:29:23 +02:00
// Abort
2019-10-08 17:07:36 +02:00
break ;
}
}
2019-10-09 21:29:23 +02:00
2020-02-05 08:00:08 +01:00
if ( sections . size ( ) = = 1 ) [ [ unlikely ] ]
2019-10-09 21:29:23 +02:00
{
return result ;
}
else
{
// NOTE: Do not disable 'cyclic ref' since the texture_barrier may have already been issued!
result . image_handle = 0 ;
result . external_subresource_desc = { 0 , deferred_request_command : : mipmap_gather , attributes , { } , tex . decoded_remap ( ) } ;
2021-11-15 20:58:57 +01:00
result . format_class = rsx : : classify_format ( attributes . gcm_format ) ;
2019-10-09 21:29:23 +02:00
if ( use_upscaling )
{
// Grab the correct image dimensions from the base mipmap level
const auto & mip0 = sections . front ( ) ;
result . external_subresource_desc . width = mip0 . dst_w ;
result . external_subresource_desc . height = mip0 . dst_h ;
}
2019-10-10 19:00:45 +02:00
const u32 cache_end = attr2 . address + ( attr2 . pitch * attr2 . height ) ;
2019-10-10 20:40:03 +02:00
result . external_subresource_desc . cache_range = utils : : address_range : : start_end ( attributes . address , cache_end ) ;
2019-10-10 19:00:45 +02:00
2019-10-09 21:29:23 +02:00
result . external_subresource_desc . sections_to_copy = std : : move ( sections ) ;
return result ;
}
2019-10-08 17:07:36 +02:00
}
// Do direct upload from CPU as the last resort
2020-12-14 19:29:07 +01:00
m_texture_upload_misses_this_frame + + ;
2019-10-08 17:07:36 +02:00
const auto subresources_layout = get_subresources_layout ( tex ) ;
2020-08-15 23:33:34 +02:00
const auto format_class = classify_format ( attributes . gcm_format ) ;
2019-10-08 17:07:36 +02:00
if ( ! tex_size )
{
2019-12-03 23:34:23 +01:00
tex_size = static_cast < u32 > ( get_texture_size ( tex ) ) ;
2019-10-08 17:07:36 +02:00
}
2019-02-25 16:03:14 +01:00
lock . upgrade ( ) ;
2018-02-21 21:43:05 +01:00
2019-10-08 17:07:36 +02:00
// Invalidate
const address_range tex_range = address_range : : start_length ( attributes . address , tex_size ) ;
2019-02-25 16:03:14 +01:00
invalidate_range_impl_base ( cmd , tex_range , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-23 17:17:54 +02:00
2019-10-08 17:07:36 +02:00
// Upload from CPU. Note that sRGB conversion is handled in the FS
auto uploaded = upload_image_from_cpu ( cmd , tex_range , attributes . width , attributes . height , attributes . depth , tex . get_exact_mipmap_count ( ) , attributes . pitch , attributes . gcm_format ,
2020-08-01 13:27:13 +02:00
texture_upload_context : : shader_read , subresources_layout , extended_dimension , attributes . swizzled ) ;
2019-10-08 17:07:36 +02:00
return { uploaded - > get_view ( tex . remap ( ) , tex . decoded_remap ( ) ) ,
texture_upload_context : : shader_read , format_class , scale , extended_dimension } ;
2017-09-08 16:52:13 +02:00
}
template < typename surface_store_type , typename blitter_type , typename . . . Args >
2018-02-10 17:21:16 +01:00
blit_op_result upload_scaled_image ( rsx : : blit_src_info & src , rsx : : blit_dst_info & dst , bool interpolate , commandbuffer_type & cmd , surface_store_type & m_rtts , blitter_type & blitter , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
2019-03-11 18:34:34 +01:00
// Since we will have dst in vram, we can 'safely' ignore the swizzle flag
// TODO: Verify correct behavior
2017-09-08 16:52:13 +02:00
bool src_is_render_target = false ;
bool dst_is_render_target = false ;
2019-03-16 11:58:15 +01:00
const bool dst_is_argb8 = ( dst . format = = rsx : : blit_engine : : transfer_destination_format : : a8r8g8b8 ) ;
const bool src_is_argb8 = ( src . format = = rsx : : blit_engine : : transfer_source_format : : a8r8g8b8 ) ;
const u8 src_bpp = src_is_argb8 ? 4 : 2 ;
const u8 dst_bpp = dst_is_argb8 ? 4 : 2 ;
2017-09-08 16:52:13 +02:00
2018-04-07 17:16:52 +02:00
typeless_xfer typeless_info = { } ;
2017-09-08 16:52:13 +02:00
image_resource_type vram_texture = 0 ;
image_resource_type dest_texture = 0 ;
2019-06-29 17:48:42 +02:00
const u32 dst_address = vm : : get_addr ( dst . pixels ) ;
u32 src_address = vm : : get_addr ( src . pixels ) ;
2017-09-08 16:52:13 +02:00
2019-03-11 18:34:34 +01:00
const f32 scale_x = fabsf ( dst . scale_x ) ;
const f32 scale_y = fabsf ( dst . scale_y ) ;
2017-09-08 16:52:13 +02:00
2019-10-20 14:43:25 +02:00
const bool is_copy_op = ( fcmp ( scale_x , 1.f ) & & fcmp ( scale_y , 1.f ) ) ;
const bool is_format_convert = ( dst_is_argb8 ! = src_is_argb8 ) ;
2020-01-23 21:20:22 +01:00
bool skip_if_collision_exists = false ;
2019-10-20 14:43:25 +02:00
2019-03-11 18:34:34 +01:00
// Offset in x and y for src is 0 (it is already accounted for when getting pixels_src)
// Reproject final clip onto source...
2019-12-03 23:34:23 +01:00
u16 src_w = static_cast < u16 > ( dst . clip_width / scale_x ) ;
u16 src_h = static_cast < u16 > ( dst . clip_height / scale_y ) ;
2018-04-07 17:16:52 +02:00
u16 dst_w = dst . clip_width ;
u16 dst_h = dst . clip_height ;
2017-09-18 19:22:34 +02:00
2019-04-11 16:16:02 +02:00
if ( true ) // This block is a debug/sanity check and should be optionally disabled with a config option
2019-03-25 14:21:19 +01:00
{
2019-04-11 16:16:02 +02:00
// Do subpixel correction in the special case of reverse scanning
// When reverse scanning, pixel0 is at offset = (dimension - 1)
if ( dst . scale_y < 0.f & & src . offset_y )
{
if ( src . offset_y = ( src . height - src . offset_y ) ;
src . offset_y = = 1 )
{
src . offset_y = 0 ;
}
}
2019-03-25 14:21:19 +01:00
2019-04-11 16:16:02 +02:00
if ( dst . scale_x < 0.f & & src . offset_x )
{
if ( src . offset_x = ( src . width - src . offset_x ) ;
src . offset_x = = 1 )
{
src . offset_x = 0 ;
}
}
2020-02-05 08:00:08 +01:00
if ( ( src_h + src . offset_y ) > src . height ) [ [ unlikely ] ]
2019-04-11 16:16:02 +02:00
{
// TODO: Special case that needs wrapping around (custom blit)
2020-02-01 09:07:25 +01:00
rsx_log . error ( " Transfer cropped in Y, src_h=%d, offset_y=%d, block_h=%d " , src_h , src . offset_y , src . height ) ;
2019-04-11 16:16:02 +02:00
src_h = src . height - src . offset_y ;
}
2020-02-05 08:00:08 +01:00
if ( ( src_w + src . offset_x ) > src . width ) [ [ unlikely ] ]
2019-04-11 16:16:02 +02:00
{
// TODO: Special case that needs wrapping around (custom blit)
2020-02-01 09:07:25 +01:00
rsx_log . error ( " Transfer cropped in X, src_w=%d, offset_x=%d, block_w=%d " , src_w , src . offset_x , src . width ) ;
2019-04-11 16:16:02 +02:00
src_w = src . width - src . offset_x ;
}
2019-03-25 14:21:19 +01:00
}
2019-03-16 11:58:15 +01:00
if ( dst . scale_y < 0.f )
{
typeless_info . flip_vertical = true ;
src_address - = ( src . pitch * ( src_h - 1 ) ) ;
}
if ( dst . scale_x < 0.f )
{
typeless_info . flip_horizontal = true ;
src_address + = ( src . width - src_w ) * src_bpp ;
}
2021-05-08 18:08:32 +02:00
auto rtt_lookup = [ & m_rtts , & cmd , & scale_x , & scale_y , this ] ( u32 address , u32 width , u32 height , u32 pitch , u8 bpp , rsx : : flags32_t access , bool allow_clipped ) - > typename surface_store_type : : surface_overlap_info
2019-03-04 12:24:15 +01:00
{
2021-05-08 18:08:32 +02:00
const auto list = m_rtts . get_merged_texture_memory_region ( cmd , address , width , height , pitch , bpp , access ) ;
2019-03-25 14:21:19 +01:00
if ( list . empty ( ) )
2019-03-04 12:24:15 +01:00
{
return { } ;
}
2019-05-13 18:39:50 +02:00
for ( auto It = list . rbegin ( ) ; It ! = list . rend ( ) ; + + It )
2019-03-25 14:21:19 +01:00
{
2019-05-14 18:50:45 +02:00
if ( ! ( It - > surface - > memory_usage_flags & rsx : : surface_usage_flags : : attachment ) )
2019-03-25 14:21:19 +01:00
{
2019-05-13 18:39:50 +02:00
// HACK
// TODO: Properly analyse the input here to determine if it can properly fit what we need
// This is a problem due to chunked transfer
// First 2 512x720 blocks go into a cpu-side buffer but suddenly when its time to render the final 256x720
// it falls onto some storage buffer in surface cache that has bad dimensions
// Proper solution is to always merge when a cpu resource is created (it should absorb the render targets in range)
// We then should not have any 'dst-is-rendertarget' surfaces in use
// Option 2: Make surfaces here part of surface cache and do not pad them for optimization
// Surface cache is good at merging for resolve operations. This keeps integrity even when drawing to the rendertgargets
// This option needs a lot more work
continue ;
}
2019-03-25 14:21:19 +01:00
2019-05-13 18:39:50 +02:00
if ( ! It - > is_clipped | | allow_clipped )
{
return * It ;
}
2019-03-25 14:21:19 +01:00
2019-10-02 18:27:48 +02:00
const auto _w = It - > dst_area . width ;
const auto _h = It - > dst_area . height ;
2019-03-25 14:21:19 +01:00
2019-05-13 18:39:50 +02:00
if ( _w < width )
{
if ( ( _w * scale_x ) < = 1.f )
continue ;
}
2019-03-25 14:21:19 +01:00
2019-05-13 18:39:50 +02:00
if ( _h < height )
{
if ( ( _h * scale_y ) < = 1.f )
continue ;
2019-03-25 14:21:19 +01:00
}
2019-05-13 18:39:50 +02:00
// Some surface exists, but its size is questionable
// Opt to re-upload (needs WCB/WDB to work properly)
break ;
2019-03-25 14:21:19 +01:00
}
2019-05-13 18:39:50 +02:00
return { } ;
2019-03-04 12:24:15 +01:00
} ;
2020-05-14 20:42:21 +02:00
auto validate_memory_range = [ ] ( u32 base_address , u32 write_end , u32 heuristic_end )
2020-05-14 13:57:58 +02:00
{
2020-05-14 20:42:21 +02:00
if ( heuristic_end < = write_end )
2020-05-14 13:57:58 +02:00
{
return true ;
}
// Confirm if the pages actually exist in vm
if ( get_location ( base_address ) = = CELL_GCM_LOCATION_LOCAL )
{
const auto vram_end = rsx : : get_current_renderer ( ) - > local_mem_size + rsx : : constants : : local_mem_base ;
2020-05-14 20:42:21 +02:00
if ( heuristic_end > vram_end )
2020-05-14 13:57:58 +02:00
{
// Outside available VRAM area
return false ;
}
}
else
{
2020-11-10 18:09:28 +01:00
if ( ! vm : : check_addr ( write_end , vm : : page_readable , ( heuristic_end - write_end ) ) )
2020-05-14 13:57:58 +02:00
{
// Enforce strict allocation size!
return false ;
}
}
return true ;
} ;
2019-03-04 12:24:15 +01:00
// Check if src/dst are parts of render targets
2019-08-08 19:37:58 +02:00
typename surface_store_type : : surface_overlap_info dst_subres ;
2019-09-04 21:19:58 +02:00
bool use_null_region = false ;
2021-05-08 18:08:32 +02:00
// TODO: Handle cases where src or dst can be a depth texture while the other is a color texture - requires a render pass to emulate
// NOTE: Grab the src first as requirements for reading are more strict than requirements for writing
auto src_subres = rtt_lookup ( src_address , src_w , src_h , src . pitch , src_bpp , surface_access : : transfer_read , false ) ;
src_is_render_target = src_subres . surface ! = nullptr ;
2019-09-06 15:39:55 +02:00
if ( get_location ( dst_address ) = = CELL_GCM_LOCATION_LOCAL )
2019-08-08 19:37:58 +02:00
{
// TODO: HACK
// After writing, it is required to lock the memory range from access!
2021-05-08 18:08:32 +02:00
dst_subres = rtt_lookup ( dst_address , dst_w , dst_h , dst . pitch , dst_bpp , surface_access : : transfer_write , false ) ;
2019-08-08 19:37:58 +02:00
dst_is_render_target = dst_subres . surface ! = nullptr ;
}
else
{
// Surface exists in local memory.
2019-10-20 14:43:25 +02:00
use_null_region = ( is_copy_op & & ! is_format_convert ) ;
2020-01-27 16:11:19 +01:00
// Invalidate surfaces in range. Sample tests should catch overlaps in theory.
m_rtts . invalidate_range ( utils : : address_range : : start_length ( dst_address , dst . pitch * dst_h ) ) ;
2019-08-08 19:37:58 +02:00
}
2017-09-18 19:22:34 +02:00
2018-04-07 17:16:52 +02:00
if ( src_is_render_target )
{
const auto surf = src_subres . surface ;
2019-03-20 23:55:30 +01:00
const auto bpp = surf - > get_bpp ( ) ;
2019-11-17 21:03:18 +01:00
const bool typeless = ( bpp ! = src_bpp | | is_format_convert ) ;
2020-02-05 08:00:08 +01:00
if ( ! typeless ) [ [ likely ] ]
2019-11-17 21:03:18 +01:00
{
// Use format as-is
2021-11-17 21:02:08 +01:00
typeless_info . src_gcm_format = helpers : : get_sized_blit_format ( src_is_argb8 , src_subres . is_depth , false ) ;
2019-11-17 21:03:18 +01:00
}
else
2018-04-07 17:16:52 +02:00
{
2019-11-17 21:03:18 +01:00
// Enable type scaling in src
2018-04-07 17:16:52 +02:00
typeless_info . src_is_typeless = true ;
2019-12-03 23:34:23 +01:00
typeless_info . src_scaling_hint = static_cast < f32 > ( bpp ) / src_bpp ;
2021-11-17 21:02:08 +01:00
typeless_info . src_gcm_format = helpers : : get_sized_blit_format ( src_is_argb8 , false , is_format_convert ) ;
2018-04-07 17:16:52 +02:00
}
2019-09-05 15:08:16 +02:00
2022-03-05 16:59:21 +01:00
if ( surf - > template get_surface_width < rsx : : surface_metrics : : pixels > ( ) ! = surf - > width ( ) | |
surf - > template get_surface_height < rsx : : surface_metrics : : pixels > ( ) ! = surf - > height ( ) )
2019-09-05 15:08:16 +02:00
{
// Must go through a scaling operation due to resolution scaling being present
2020-12-09 08:47:45 +01:00
ensure ( g_cfg . video . resolution_scale_percent ! = 100 ) ;
2019-09-05 15:08:16 +02:00
use_null_region = false ;
}
2018-04-07 17:16:52 +02:00
}
2020-01-27 16:11:19 +01:00
else
{
// Determine whether to perform this transfer on CPU or GPU (src data may not be graphical)
const bool is_trivial_copy = is_copy_op & & ! is_format_convert & & ! dst . swizzled ;
const bool is_block_transfer = ( dst_w = = src_w & & dst_h = = src_h & & ( src . pitch = = dst . pitch | | src_h = = 1 ) ) ;
const bool is_mirror_op = ( dst . scale_x < 0.f | | dst . scale_y < 0.f ) ;
if ( dst_is_render_target )
{
if ( is_trivial_copy & & src_h = = 1 )
{
dst_is_render_target = false ;
dst_subres = { } ;
}
}
// Always use GPU blit if src or dst is in the surface store
if ( ! dst_is_render_target )
{
if ( is_trivial_copy )
{
// Check if trivial memcpy can perform the same task
// Used to copy programs and arbitrary data to the GPU in some cases
// NOTE: This case overrides the GPU texture scaling option
if ( is_block_transfer & & ! is_mirror_op )
{
return false ;
}
// If a matching section exists with a different use-case, fall back to CPU memcpy
skip_if_collision_exists = true ;
}
if ( ! g_cfg . video . use_gpu_texture_scaling )
{
if ( dst . swizzled )
{
// Swizzle operation requested. Use fallback
return false ;
}
if ( is_trivial_copy & & get_location ( dst_address ) ! = CELL_GCM_LOCATION_LOCAL )
{
// Trivial copy and the destination is in XDR memory
return false ;
}
}
}
}
2018-04-07 17:16:52 +02:00
if ( dst_is_render_target )
{
2019-11-17 21:03:18 +01:00
const auto bpp = dst_subres . surface - > get_bpp ( ) ;
const bool typeless = ( bpp ! = dst_bpp | | is_format_convert ) ;
2020-02-05 08:00:08 +01:00
if ( ! typeless ) [ [ likely ] ]
2018-04-07 17:16:52 +02:00
{
2021-11-17 21:02:08 +01:00
typeless_info . dst_gcm_format = helpers : : get_sized_blit_format ( dst_is_argb8 , dst_subres . is_depth , false ) ;
2019-11-17 21:03:18 +01:00
}
else
{
// Enable type scaling in dst
2018-04-07 17:16:52 +02:00
typeless_info . dst_is_typeless = true ;
2019-12-03 23:34:23 +01:00
typeless_info . dst_scaling_hint = static_cast < f32 > ( bpp ) / dst_bpp ;
2021-11-17 21:02:08 +01:00
typeless_info . dst_gcm_format = helpers : : get_sized_blit_format ( dst_is_argb8 , false , is_format_convert ) ;
2018-04-07 17:16:52 +02:00
}
}
2019-03-07 16:17:00 +01:00
section_storage_type * cached_dest = nullptr ;
2021-10-26 22:43:27 +02:00
section_storage_type * cached_src = nullptr ;
2019-11-17 21:03:18 +01:00
bool dst_is_depth_surface = false ;
2017-10-12 14:48:31 +02:00
u16 max_dst_width = dst . width ;
u16 max_dst_height = dst . height ;
areai src_area = { 0 , 0 , src_w , src_h } ;
2018-04-07 17:16:52 +02:00
areai dst_area = { 0 , 0 , dst_w , dst_h } ;
2017-10-12 14:48:31 +02:00
2022-01-09 21:07:18 +01:00
size2i dst_dimensions = { static_cast < s32 > ( dst . pitch / dst_bpp ) , dst . height } ;
2020-03-07 20:20:38 +01:00
position2i dst_offset = { dst . offset_x , dst . offset_y } ;
u32 dst_base_address = dst . rsx_address ;
2019-09-12 16:33:13 +02:00
const auto src_payload_length = ( src . pitch * ( src_h - 1 ) + ( src_w * src_bpp ) ) ;
const auto dst_payload_length = ( dst . pitch * ( dst_h - 1 ) + ( dst_w * dst_bpp ) ) ;
2020-03-07 20:20:38 +01:00
const auto dst_range = address_range : : start_length ( dst_address , dst_payload_length ) ;
2019-09-12 16:33:13 +02:00
2020-03-07 20:20:38 +01:00
if ( ! use_null_region & & ! dst_is_render_target )
2017-10-24 15:22:53 +02:00
{
2020-03-07 20:20:38 +01:00
size2u src_dimensions = { 0 , 0 } ;
if ( src_is_render_target )
2019-03-11 18:34:34 +01:00
{
2022-03-05 16:59:21 +01:00
src_dimensions . width = src_subres . surface - > template get_surface_width < rsx : : surface_metrics : : samples > ( ) ;
src_dimensions . height = src_subres . surface - > template get_surface_height < rsx : : surface_metrics : : samples > ( ) ;
2019-03-11 18:34:34 +01:00
}
2020-03-07 20:20:38 +01:00
const auto props = texture_cache_helpers : : get_optimal_blit_target_properties (
src_is_render_target ,
dst_range ,
dst . pitch ,
src_dimensions ,
static_cast < size2u > ( dst_dimensions )
) ;
if ( props . use_dma_region )
2019-03-11 18:34:34 +01:00
{
2020-03-07 20:20:38 +01:00
// Try to use a dma flush
use_null_region = ( is_copy_op & & ! is_format_convert ) ;
2019-03-25 14:21:19 +01:00
}
else
{
2020-03-07 20:20:38 +01:00
if ( props . offset )
{
// Calculate new offsets
dst_base_address = props . offset ;
const auto new_offset = ( dst_address - dst_base_address ) ;
// Generate new offsets
dst_offset . y = new_offset / dst . pitch ;
dst_offset . x = ( new_offset % dst . pitch ) / dst_bpp ;
}
dst_dimensions . width = static_cast < s32 > ( props . width ) ;
dst_dimensions . height = static_cast < s32 > ( props . height ) ;
2019-03-11 18:34:34 +01:00
}
2017-10-24 15:22:53 +02:00
}
2017-10-12 14:48:31 +02:00
2019-03-07 16:17:00 +01:00
reader_lock lock ( m_cache_mutex ) ;
2017-09-14 13:37:14 +02:00
2019-03-22 12:10:29 +01:00
const auto old_dst_area = dst_area ;
2017-09-08 16:52:13 +02:00
if ( ! dst_is_render_target )
{
2018-09-10 12:22:24 +02:00
// Check for any available region that will fit this one
2020-01-23 21:20:22 +01:00
u32 required_type_mask ;
if ( use_null_region )
{
required_type_mask = texture_upload_context : : dma ;
}
else
{
required_type_mask = texture_upload_context : : blit_engine_dst ;
if ( skip_if_collision_exists ) required_type_mask | = texture_upload_context : : shader_read ;
}
auto overlapping_surfaces = find_texture_from_range ( dst_range , dst . pitch , required_type_mask ) ;
2018-02-10 09:52:44 +01:00
for ( const auto & surface : overlapping_surfaces )
2017-09-08 16:52:13 +02:00
{
2019-03-04 19:22:45 +01:00
if ( ! surface - > is_locked ( ) )
{
2019-03-07 16:17:00 +01:00
// Discard
2019-03-04 19:22:45 +01:00
surface - > set_dirty ( true ) ;
continue ;
}
if ( cached_dest )
{
// Nothing to do
continue ;
}
2020-01-23 21:20:22 +01:00
if ( ! dst_range . inside ( surface - > get_section_range ( ) ) )
{
// Hit test failed
continue ;
}
2019-09-04 21:19:58 +02:00
if ( use_null_region )
{
2020-01-23 21:20:22 +01:00
// Attach to existing region
cached_dest = surface ;
2019-09-04 21:19:58 +02:00
2019-09-12 16:33:13 +02:00
// Technically it is totally possible to just extend a pre-existing section
// Will leave this as a TODO
2019-09-04 21:19:58 +02:00
continue ;
}
2020-02-05 08:00:08 +01:00
if ( skip_if_collision_exists ) [ [ unlikely ] ]
2019-03-04 19:22:45 +01:00
{
2020-01-23 21:20:22 +01:00
if ( surface - > get_context ( ) ! = texture_upload_context : : blit_engine_dst )
{
// This section is likely to be 'flushed' to CPU for reupload soon anyway
return false ;
}
2019-03-04 19:22:45 +01:00
}
2020-08-15 19:28:35 +02:00
// Prefer formats which will not trigger a typeless conversion later
// Only color formats are supported as destination as most access from blit engine will be color
2019-11-17 21:03:18 +01:00
switch ( surface - > get_gcm_format ( ) )
{
case CELL_GCM_TEXTURE_A8R8G8B8 :
if ( ! dst_is_argb8 ) continue ;
break ;
case CELL_GCM_TEXTURE_R5G6B5 :
if ( dst_is_argb8 ) continue ;
break ;
default :
continue ;
}
2020-01-23 21:20:22 +01:00
if ( const auto this_address = surface - > get_section_base ( ) ;
const u32 address_offset = dst_address - this_address )
2017-09-08 16:52:13 +02:00
{
2022-01-09 21:07:18 +01:00
const u32 offset_y = address_offset / dst . pitch ;
const u32 offset_x = address_offset % dst . pitch ;
const u32 offset_x_in_block = offset_x / dst_bpp ;
2017-09-19 14:46:16 +02:00
dst_area . x1 + = offset_x_in_block ;
dst_area . x2 + = offset_x_in_block ;
dst_area . y1 + = offset_y ;
dst_area . y2 + = offset_y ;
}
2017-09-08 16:52:13 +02:00
2018-09-10 12:22:24 +02:00
// Validate clipping region
2019-12-03 23:34:23 +01:00
if ( static_cast < uint > ( dst_area . x2 ) < = surface - > get_width ( ) & &
static_cast < uint > ( dst_area . y2 ) < = surface - > get_height ( ) )
2017-09-19 14:46:16 +02:00
{
cached_dest = surface ;
2019-03-07 16:17:00 +01:00
dest_texture = cached_dest - > get_raw_texture ( ) ;
typeless_info . dst_context = cached_dest - > get_context ( ) ;
max_dst_width = cached_dest - > get_width ( ) ;
max_dst_height = cached_dest - > get_height ( ) ;
2019-03-04 19:22:45 +01:00
continue ;
2017-09-19 14:46:16 +02:00
}
2018-09-10 12:22:24 +02:00
dst_area = old_dst_area ;
2017-09-08 16:52:13 +02:00
}
2019-11-17 21:03:18 +01:00
2019-11-18 19:39:40 +01:00
if ( cached_dest & & cached_dest - > get_context ( ) ! = texture_upload_context : : dma )
2019-11-17 21:03:18 +01:00
{
2019-11-18 19:39:40 +01:00
// NOTE: DMA sections are plain memory blocks with no format!
2020-02-05 08:00:08 +01:00
if ( cached_dest ) [ [ likely ] ]
2019-11-18 19:39:40 +01:00
{
typeless_info . dst_gcm_format = cached_dest - > get_gcm_format ( ) ;
dst_is_depth_surface = cached_dest - > is_depth_texture ( ) ;
}
2019-11-17 21:03:18 +01:00
}
2017-09-08 16:52:13 +02:00
}
else
{
2019-03-04 12:24:15 +01:00
// Destination dimensions are relaxed (true)
2019-10-02 18:27:48 +02:00
dst_area = dst_subres . src_area ;
2017-09-08 16:52:13 +02:00
2021-05-08 18:08:32 +02:00
dest_texture = dst_subres . surface - > get_surface ( rsx : : surface_access : : transfer_write ) ;
2018-07-26 19:52:22 +02:00
typeless_info . dst_context = texture_upload_context : : framebuffer_storage ;
2019-11-17 21:03:18 +01:00
dst_is_depth_surface = typeless_info . dst_is_typeless ? false : dst_subres . is_depth ;
2017-09-08 16:52:13 +02:00
2022-03-05 16:59:21 +01:00
max_dst_width = static_cast < u16 > ( dst_subres . surface - > template get_surface_width < rsx : : surface_metrics : : samples > ( ) * typeless_info . dst_scaling_hint ) ;
max_dst_height = dst_subres . surface - > template get_surface_height < rsx : : surface_metrics : : samples > ( ) ;
2017-09-08 16:52:13 +02:00
}
2019-02-25 16:03:14 +01:00
// Create source texture if does not exist
2019-09-11 01:53:25 +02:00
// TODO: This can be greatly improved with DMA optimizations. Most transfer operations here are actually non-graphical (no transforms applied)
2017-09-08 16:52:13 +02:00
if ( ! src_is_render_target )
{
2019-09-11 01:53:25 +02:00
// NOTE: Src address already takes into account the flipped nature of the overlap!
2019-09-09 18:55:58 +02:00
const u32 lookup_mask = rsx : : texture_upload_context : : blit_engine_src | rsx : : texture_upload_context : : blit_engine_dst | rsx : : texture_upload_context : : shader_read ;
2019-09-12 16:33:13 +02:00
auto overlapping_surfaces = find_texture_from_range < false > ( address_range : : start_length ( src_address , src_payload_length ) , src . pitch , lookup_mask ) ;
2017-11-10 13:13:32 +01:00
auto old_src_area = src_area ;
2019-11-17 21:03:18 +01:00
2018-02-10 09:52:44 +01:00
for ( const auto & surface : overlapping_surfaces )
2017-09-08 16:52:13 +02:00
{
2019-03-07 16:17:00 +01:00
if ( ! surface - > is_locked ( ) )
{
// TODO: Rejecting unlocked blit_engine dst causes stutter in SCV
// Surfaces marked as dirty have already been removed, leaving only flushed blit_dst data
2019-03-11 18:34:34 +01:00
continue ;
2019-03-07 16:17:00 +01:00
}
2019-09-09 18:55:58 +02:00
// Force format matching; only accept 16-bit data for 16-bit transfers, 32-bit for 32-bit transfers
switch ( surface - > get_gcm_format ( ) )
{
case CELL_GCM_TEXTURE_X32_FLOAT :
case CELL_GCM_TEXTURE_Y16_X16 :
case CELL_GCM_TEXTURE_Y16_X16_FLOAT :
{
2021-11-17 21:02:08 +01:00
// Should be copy compatible but not scaling compatible
if ( src_is_argb8 & & ( is_copy_op | | dst_is_render_target ) ) break ;
continue ;
}
case CELL_GCM_TEXTURE_DEPTH24_D8 :
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT :
{
// Should be copy compatible but not scaling compatible
if ( src_is_argb8 & & ( is_copy_op | | ! dst_is_render_target ) ) break ;
continue ;
}
case CELL_GCM_TEXTURE_A8R8G8B8 :
case CELL_GCM_TEXTURE_D8R8G8B8 :
{
// Perfect match
if ( src_is_argb8 ) break ;
continue ;
2019-09-09 18:55:58 +02:00
}
case CELL_GCM_TEXTURE_X16 :
case CELL_GCM_TEXTURE_G8B8 :
case CELL_GCM_TEXTURE_A1R5G5B5 :
case CELL_GCM_TEXTURE_A4R4G4B4 :
case CELL_GCM_TEXTURE_D1R5G5B5 :
case CELL_GCM_TEXTURE_R5G5B5A1 :
{
2021-11-17 21:02:08 +01:00
// Copy compatible
if ( ! src_is_argb8 & & ( is_copy_op | | dst_is_render_target ) ) break ;
continue ;
}
case CELL_GCM_TEXTURE_DEPTH16 :
case CELL_GCM_TEXTURE_DEPTH16_FLOAT :
{
// Copy compatible
if ( ! src_is_argb8 & & ( is_copy_op | | ! dst_is_render_target ) ) break ;
continue ;
}
case CELL_GCM_TEXTURE_R5G6B5 :
{
// Perfect match
if ( ! src_is_argb8 ) break ;
continue ;
2019-09-09 18:55:58 +02:00
}
default :
{
continue ;
}
}
2019-03-04 19:22:45 +01:00
const auto this_address = surface - > get_section_base ( ) ;
if ( this_address > src_address )
{
continue ;
}
if ( const u32 address_offset = src_address - this_address )
2017-11-10 13:13:32 +01:00
{
2022-01-09 21:07:18 +01:00
const u32 offset_y = address_offset / src . pitch ;
const u32 offset_x = address_offset % src . pitch ;
const u32 offset_x_in_block = offset_x / src_bpp ;
2017-11-10 13:13:32 +01:00
src_area . x1 + = offset_x_in_block ;
src_area . x2 + = offset_x_in_block ;
src_area . y1 + = offset_y ;
src_area . y2 + = offset_y ;
}
if ( src_area . x2 < = surface - > get_width ( ) & &
src_area . y2 < = surface - > get_height ( ) )
{
2019-11-17 21:03:18 +01:00
cached_src = surface ;
2017-11-10 13:13:32 +01:00
break ;
}
src_area = old_src_area ;
2017-09-08 16:52:13 +02:00
}
2017-11-10 13:13:32 +01:00
2019-11-17 21:03:18 +01:00
if ( ! cached_src )
2017-09-08 16:52:13 +02:00
{
2019-04-11 16:16:02 +02:00
const u16 full_width = src . pitch / src_bpp ;
u32 image_base = src . rsx_address ;
u16 image_width = full_width ;
u16 image_height = src . height ;
2020-05-14 13:57:58 +02:00
// Check if memory is valid
const bool use_full_range = validate_memory_range (
image_base ,
( src_address + src_payload_length ) ,
image_base + ( image_height * src . pitch ) ) ;
if ( use_full_range & & dst . scale_x > 0.f & & dst . scale_y > 0.f ) [ [ likely ] ]
2019-04-11 16:16:02 +02:00
{
// Loading full image from the corner address
// Translate src_area into the declared block
src_area . x1 + = src . offset_x ;
src_area . x2 + = src . offset_x ;
src_area . y1 + = src . offset_y ;
src_area . y2 + = src . offset_y ;
}
else
{
image_base = src_address ;
image_height = src_h ;
}
2019-03-25 14:21:19 +01:00
2020-08-15 23:33:34 +02:00
std : : vector < rsx : : subresource_layout > subresource_layout ;
rsx : : subresource_layout subres = { } ;
2019-10-28 19:06:15 +01:00
subres . width_in_block = subres . width_in_texel = image_width ;
subres . height_in_block = subres . height_in_texel = image_height ;
2019-04-11 16:16:02 +02:00
subres . pitch_in_block = full_width ;
2017-09-08 16:52:13 +02:00
subres . depth = 1 ;
2021-05-30 16:10:46 +02:00
subres . data = { vm : : _ptr < const std : : byte > ( image_base ) , static_cast < std : : span < const std : : byte > : : size_type > ( src . pitch * image_height ) } ;
2017-09-08 16:52:13 +02:00
subresource_layout . push_back ( subres ) ;
2021-11-17 21:02:08 +01:00
const u32 gcm_format = helpers : : get_sized_blit_format ( src_is_argb8 , dst_is_depth_surface , is_format_convert ) ;
2019-11-17 21:03:18 +01:00
const auto rsx_range = address_range : : start_length ( image_base , src . pitch * image_height ) ;
lock . upgrade ( ) ;
invalidate_range_impl_base ( cmd , rsx_range , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
cached_src = upload_image_from_cpu ( cmd , rsx_range , image_width , image_height , 1 , 1 , src . pitch , gcm_format , texture_upload_context : : blit_engine_src ,
subresource_layout , rsx : : texture_dimension_extended : : texture_dimension_2d , dst . swizzled ) ;
2017-10-21 23:12:32 +02:00
2019-11-17 21:03:18 +01:00
typeless_info . src_gcm_format = gcm_format ;
2017-09-08 16:52:13 +02:00
}
2022-01-25 00:13:09 +01:00
else
2019-11-17 21:03:18 +01:00
{
typeless_info . src_gcm_format = cached_src - > get_gcm_format ( ) ;
}
2021-10-26 22:43:27 +02:00
cached_src - > add_ref ( ) ;
2019-11-17 21:03:18 +01:00
vram_texture = cached_src - > get_raw_texture ( ) ;
typeless_info . src_context = cached_src - > get_context ( ) ;
2017-09-08 16:52:13 +02:00
}
else
{
2019-10-02 18:27:48 +02:00
src_area = src_subres . src_area ;
2021-05-08 18:08:32 +02:00
vram_texture = src_subres . surface - > get_surface ( rsx : : surface_access : : transfer_read ) ;
2018-07-26 19:52:22 +02:00
typeless_info . src_context = texture_upload_context : : framebuffer_storage ;
2017-09-08 16:52:13 +02:00
}
2021-01-12 11:01:06 +01:00
//const auto src_is_depth_format = helpers::is_gcm_depth_format(typeless_info.src_gcm_format);
2021-11-17 21:02:08 +01:00
const auto preferred_dst_format = helpers : : get_sized_blit_format ( dst_is_argb8 , false , is_format_convert ) ;
2019-11-18 20:13:29 +01:00
2019-09-04 21:19:58 +02:00
if ( cached_dest & & ! use_null_region )
2017-12-07 13:08:11 +01:00
{
2019-03-04 19:22:45 +01:00
// Prep surface
2021-05-28 00:44:07 +02:00
auto channel_order = src_is_render_target ? rsx : : component_order : : native :
dst_is_argb8 ? rsx : : component_order : : default_ :
rsx : : component_order : : swapped_native ;
2017-12-07 18:46:49 +01:00
2021-05-28 00:44:07 +02:00
set_component_order ( * cached_dest , preferred_dst_format , channel_order ) ;
2017-12-07 13:08:11 +01:00
}
2019-03-04 19:22:45 +01:00
// Validate clipping region
2017-09-08 16:52:13 +02:00
if ( ( dst . offset_x + dst . clip_x + dst . clip_width ) > max_dst_width ) dst . clip_x = 0 ;
if ( ( dst . offset_y + dst . clip_y + dst . clip_height ) > max_dst_height ) dst . clip_y = 0 ;
2019-03-04 19:22:45 +01:00
// Reproject clip offsets onto source to simplify blit
2017-09-08 16:52:13 +02:00
if ( dst . clip_x | | dst . clip_y )
{
2019-12-03 23:34:23 +01:00
const u16 scaled_clip_offset_x = static_cast < u16 > ( dst . clip_x / ( scale_x * typeless_info . src_scaling_hint ) ) ;
const u16 scaled_clip_offset_y = static_cast < u16 > ( dst . clip_y / scale_y ) ;
2017-09-08 16:52:13 +02:00
src_area . x1 + = scaled_clip_offset_x ;
src_area . x2 + = scaled_clip_offset_x ;
src_area . y1 + = scaled_clip_offset_y ;
src_area . y2 + = scaled_clip_offset_y ;
}
2019-09-04 21:19:58 +02:00
if ( ! cached_dest & & ! dst_is_render_target )
2017-09-08 16:52:13 +02:00
{
2020-12-09 08:47:45 +01:00
ensure ( ! dest_texture ) ;
2018-09-18 20:06:34 +02:00
2019-06-12 20:02:51 +02:00
// Need to calculate the minimum required size that will fit the data, anchored on the rsx_address
2018-09-18 20:06:34 +02:00
// If the application starts off with an 'inseted' section, the guessed dimensions may not fit!
2019-09-12 16:33:13 +02:00
const u32 write_end = dst_address + dst_payload_length ;
2020-03-07 20:20:38 +01:00
u32 block_end = dst_base_address + ( dst . pitch * dst_dimensions . height ) ;
2019-09-12 16:33:13 +02:00
// Confirm if the pages actually exist in vm
2020-05-14 13:57:58 +02:00
if ( ! validate_memory_range ( dst_base_address , write_end , block_end ) )
2019-09-12 16:33:13 +02:00
{
2020-05-14 13:57:58 +02:00
block_end = write_end ;
2019-09-12 16:33:13 +02:00
}
2018-09-18 20:06:34 +02:00
2020-03-07 20:20:38 +01:00
const u32 usable_section_length = std : : max ( write_end , block_end ) - dst_base_address ;
2020-01-16 12:35:49 +01:00
dst_dimensions . height = align2 ( usable_section_length , dst . pitch ) / dst . pitch ;
const u32 full_section_length = ( ( dst_dimensions . height - 1 ) * dst . pitch ) + ( dst_dimensions . width * dst_bpp ) ;
2020-03-07 20:20:38 +01:00
const auto rsx_range = address_range : : start_length ( dst_base_address , full_section_length ) ;
2017-09-14 13:37:14 +02:00
2019-08-08 19:37:58 +02:00
lock . upgrade ( ) ;
// NOTE: Write flag set to remove all other overlapping regions (e.g shader_read or blit_src)
2021-10-26 22:43:27 +02:00
// NOTE: This step can potentially invalidate the newly created src image as well.
2019-08-08 19:37:58 +02:00
invalidate_range_impl_base ( cmd , rsx_range , invalidation_cause : : write , std : : forward < Args > ( extras ) . . . ) ;
2020-02-05 08:00:08 +01:00
if ( use_null_region ) [ [ likely ] ]
2019-03-13 13:38:58 +01:00
{
2019-09-04 21:19:58 +02:00
bool force_dma_load = false ;
if ( ( dst_w * dst_bpp ) ! = dst . pitch )
{
// Keep Cell from touching the range we need
2019-09-12 16:33:13 +02:00
const auto prot_range = dst_range . to_page_range ( ) ;
2019-09-04 21:19:58 +02:00
utils : : memory_protect ( vm : : base ( prot_range . start ) , prot_range . length ( ) , utils : : protection : : no ) ;
force_dma_load = true ;
}
cached_dest = create_nul_section ( cmd , rsx_range , force_dma_load ) ;
2019-03-13 13:38:58 +01:00
}
else
{
2019-09-04 21:19:58 +02:00
// render target data is already in correct swizzle layout
2021-05-28 00:44:07 +02:00
auto channel_order = src_is_render_target ? rsx : : component_order : : native :
dst_is_argb8 ? rsx : : component_order : : default_ :
rsx : : component_order : : swapped_native ;
2019-08-08 19:37:58 +02:00
2019-09-04 21:19:58 +02:00
// Translate dst_area into the 'full' dst block based on dst.rsx_address as (0, 0)
2020-03-07 20:20:38 +01:00
dst_area . x1 + = dst_offset . x ;
dst_area . x2 + = dst_offset . x ;
dst_area . y1 + = dst_offset . y ;
dst_area . y2 + = dst_offset . y ;
2019-03-13 13:38:58 +01:00
2019-09-04 21:19:58 +02:00
if ( ! dst_area . x1 & & ! dst_area . y1 & & dst_area . x2 = = dst_dimensions . width & & dst_area . y2 = = dst_dimensions . height )
{
cached_dest = create_new_texture ( cmd , rsx_range , dst_dimensions . width , dst_dimensions . height , 1 , 1 , dst . pitch ,
2019-11-17 21:03:18 +01:00
preferred_dst_format , rsx : : texture_upload_context : : blit_engine_dst , rsx : : texture_dimension_extended : : texture_dimension_2d ,
2021-05-28 00:44:07 +02:00
false , channel_order , 0 ) ;
2019-09-04 21:19:58 +02:00
}
else
{
// HACK: workaround for data race with Cell
// Pre-lock the memory range we'll be touching, then load with super_ptr
2019-09-12 16:33:13 +02:00
const auto prot_range = dst_range . to_page_range ( ) ;
2019-09-04 21:19:58 +02:00
utils : : memory_protect ( vm : : base ( prot_range . start ) , prot_range . length ( ) , utils : : protection : : no ) ;
2022-01-09 21:07:18 +01:00
const auto pitch_in_block = dst . pitch / dst_bpp ;
2020-08-15 23:33:34 +02:00
std : : vector < rsx : : subresource_layout > subresource_layout ;
rsx : : subresource_layout subres = { } ;
2019-10-28 19:06:15 +01:00
subres . width_in_block = subres . width_in_texel = dst_dimensions . width ;
subres . height_in_block = subres . height_in_texel = dst_dimensions . height ;
2019-09-04 21:19:58 +02:00
subres . pitch_in_block = pitch_in_block ;
subres . depth = 1 ;
2021-05-30 16:10:46 +02:00
subres . data = { vm : : get_super_ptr < const std : : byte > ( dst_base_address ) , static_cast < std : : span < const std : : byte > : : size_type > ( dst . pitch * dst_dimensions . height ) } ;
2019-09-04 21:19:58 +02:00
subresource_layout . push_back ( subres ) ;
cached_dest = upload_image_from_cpu ( cmd , rsx_range , dst_dimensions . width , dst_dimensions . height , 1 , 1 , dst . pitch ,
2019-11-17 21:03:18 +01:00
preferred_dst_format , rsx : : texture_upload_context : : blit_engine_dst , subresource_layout ,
2019-09-04 21:19:58 +02:00
rsx : : texture_dimension_extended : : texture_dimension_2d , false ) ;
2021-05-28 00:44:07 +02:00
set_component_order ( * cached_dest , preferred_dst_format , channel_order ) ;
2019-09-04 21:19:58 +02:00
}
2019-03-13 13:38:58 +01:00
2019-09-04 21:19:58 +02:00
dest_texture = cached_dest - > get_raw_texture ( ) ;
typeless_info . dst_context = texture_upload_context : : blit_engine_dst ;
2020-08-15 19:28:35 +02:00
typeless_info . dst_gcm_format = preferred_dst_format ;
2019-03-13 13:38:58 +01:00
}
2017-09-08 16:52:13 +02:00
}
2018-05-10 13:50:32 +02:00
2020-12-09 08:47:45 +01:00
ensure ( cached_dest | | dst_is_render_target ) ;
2019-04-29 13:37:30 +02:00
2019-04-01 17:45:19 +02:00
// Invalidate any cached subresources in modified range
2019-09-12 16:33:13 +02:00
notify_surface_changed ( dst_range ) ;
2018-09-10 12:22:24 +02:00
2020-08-01 13:27:13 +02:00
// What type of data is being moved?
const auto raster_type = src_is_render_target ? src_subres . surface - > raster_type : rsx : : surface_raster_type : : undefined ;
2019-04-01 17:45:19 +02:00
if ( cached_dest )
{
2019-09-12 16:33:13 +02:00
// Validate modified range
u32 mem_offset = dst_address - cached_dest - > get_section_base ( ) ;
2020-12-09 08:47:45 +01:00
ensure ( ( mem_offset + dst_payload_length ) < = cached_dest - > get_section_size ( ) ) ;
2018-09-10 12:22:24 +02:00
2019-09-12 16:33:13 +02:00
lock . upgrade ( ) ;
2018-09-10 12:22:24 +02:00
2019-09-12 16:33:13 +02:00
cached_dest - > reprotect ( utils : : protection : : no , { mem_offset , dst_payload_length } ) ;
2018-09-19 00:21:57 +02:00
cached_dest - > touch ( m_cache_update_tag ) ;
update_cache_tag ( ) ;
2020-08-01 13:27:13 +02:00
// Set swizzle flag
cached_dest - > set_swizzled ( raster_type = = rsx : : surface_raster_type : : swizzle ) ;
2018-02-10 17:21:16 +01:00
}
2018-05-10 13:50:32 +02:00
else
{
2019-08-08 19:37:58 +02:00
// NOTE: This doesn't work very well in case of Cell access
// Need to lock the affected memory range and actually attach this subres to a locked_region
2020-08-01 13:27:13 +02:00
dst_subres . surface - > on_write_copy ( rsx : : get_shared_tag ( ) , false , raster_type ) ;
2019-11-17 21:30:21 +01:00
2020-01-15 14:12:23 +01:00
// Reset this object's synchronization status if it is locked
lock . upgrade ( ) ;
2021-01-26 21:46:32 +01:00
if ( const auto found = find_cached_texture ( dst_subres . surface - > get_memory_range ( ) , { . gcm_format = RSX_GCM_FORMAT_IGNORED } , false , false , false ) )
2020-01-15 14:12:23 +01:00
{
if ( found - > is_locked ( ) )
{
2020-03-14 21:23:19 +01:00
if ( found - > get_rsx_pitch ( ) = = dst . pitch )
{
// It is possible for other resource types to overlap this fbo if it only covers a small section of its max width.
// Blit engine read and write resources do not allow clipping and would have been recreated at the same address.
// TODO: In cases of clipped data, generate the blit resources in the surface cache instead.
if ( found - > get_context ( ) = = rsx : : texture_upload_context : : framebuffer_storage )
{
found - > touch ( m_cache_update_tag ) ;
update_cache_tag ( ) ;
}
}
else
{
// Unlikely situation, but the only one which would allow re-upload from CPU to overlap this section.
2020-12-09 08:47:45 +01:00
ensure ( ! found - > is_flushable ( ) ) ;
2020-03-14 21:23:19 +01:00
found - > discard ( true ) ;
}
2020-01-15 14:12:23 +01:00
}
}
2018-05-10 13:50:32 +02:00
}
2017-09-08 16:52:13 +02:00
2019-06-06 12:25:32 +02:00
if ( src_is_render_target )
2019-05-20 16:14:02 +02:00
{
2022-03-05 16:59:21 +01:00
const auto surface_width = src_subres . surface - > template get_surface_width < rsx : : surface_metrics : : pixels > ( ) ;
const auto surface_height = src_subres . surface - > template get_surface_height < rsx : : surface_metrics : : pixels > ( ) ;
2020-11-29 17:30:55 +01:00
std : : tie ( src_area . x1 , src_area . y1 ) = rsx : : apply_resolution_scale < false > ( src_area . x1 , src_area . y1 , surface_width , surface_height ) ;
std : : tie ( src_area . x2 , src_area . y2 ) = rsx : : apply_resolution_scale < true > ( src_area . x2 , src_area . y2 , surface_width , surface_height ) ;
2019-06-04 18:01:53 +02:00
// The resource is of surface type; possibly disabled AA emulation
2021-05-08 18:08:32 +02:00
src_subres . surface - > transform_blit_coordinates ( rsx : : surface_access : : transfer_read , src_area ) ;
2019-05-20 16:14:02 +02:00
}
2019-06-06 12:25:32 +02:00
if ( dst_is_render_target )
2019-05-20 16:14:02 +02:00
{
2022-03-05 16:59:21 +01:00
const auto surface_width = dst_subres . surface - > template get_surface_width < rsx : : surface_metrics : : pixels > ( ) ;
const auto surface_height = dst_subres . surface - > template get_surface_height < rsx : : surface_metrics : : pixels > ( ) ;
2020-11-29 17:30:55 +01:00
std : : tie ( dst_area . x1 , dst_area . y1 ) = rsx : : apply_resolution_scale < false > ( dst_area . x1 , dst_area . y1 , surface_width , surface_height ) ;
std : : tie ( dst_area . x2 , dst_area . y2 ) = rsx : : apply_resolution_scale < true > ( dst_area . x2 , dst_area . y2 , surface_width , surface_height ) ;
2019-06-04 18:01:53 +02:00
// The resource is of surface type; possibly disabled AA emulation
2021-05-08 18:08:32 +02:00
dst_subres . surface - > transform_blit_coordinates ( rsx : : surface_access : : transfer_write , dst_area ) ;
2019-05-20 16:14:02 +02:00
}
2020-08-15 19:28:35 +02:00
if ( helpers : : is_gcm_depth_format ( typeless_info . src_gcm_format ) ! =
helpers : : is_gcm_depth_format ( typeless_info . dst_gcm_format ) )
2019-09-04 21:19:58 +02:00
{
2020-09-21 23:00:11 +02:00
// Make the depth side typeless because the other side is guaranteed to be color
if ( helpers : : is_gcm_depth_format ( typeless_info . src_gcm_format ) )
2020-03-03 20:54:00 +01:00
{
2020-09-21 23:00:11 +02:00
// SRC is depth, transfer must be done typelessly
if ( ! typeless_info . src_is_typeless )
2020-03-03 20:54:00 +01:00
{
2020-08-15 19:28:35 +02:00
typeless_info . src_is_typeless = true ;
2021-11-17 21:02:08 +01:00
typeless_info . src_gcm_format = helpers : : get_sized_blit_format ( src_is_argb8 , false , false ) ;
2020-08-15 19:28:35 +02:00
}
2020-09-21 23:00:11 +02:00
}
else
{
// DST is depth, transfer must be done typelessly
if ( ! typeless_info . dst_is_typeless )
2020-08-15 19:28:35 +02:00
{
typeless_info . dst_is_typeless = true ;
2021-11-17 21:02:08 +01:00
typeless_info . dst_gcm_format = helpers : : get_sized_blit_format ( dst_is_argb8 , false , false ) ;
2020-03-03 20:54:00 +01:00
}
}
2020-08-15 19:28:35 +02:00
}
if ( ! use_null_region ) [ [ likely ] ]
{
// Do preliminary analysis
typeless_info . analyse ( ) ;
2020-03-03 20:54:00 +01:00
2019-11-17 21:03:18 +01:00
blitter . scale_image ( cmd , vram_texture , dest_texture , src_area , dst_area , interpolate , typeless_info ) ;
2019-09-04 21:19:58 +02:00
}
2021-11-15 18:30:44 +01:00
else if ( cached_dest )
2019-09-04 21:19:58 +02:00
{
2019-09-12 16:33:13 +02:00
cached_dest - > dma_transfer ( cmd , vram_texture , src_area , dst_range , dst . pitch ) ;
2019-09-04 21:19:58 +02:00
}
2018-02-10 17:21:16 +01:00
2021-10-26 22:43:27 +02:00
if ( cached_src )
{
cached_src - > release ( ) ;
}
2018-02-10 17:21:16 +01:00
blit_op_result result = true ;
if ( cached_dest )
{
result . real_dst_address = cached_dest - > get_section_base ( ) ;
result . real_dst_size = cached_dest - > get_section_size ( ) ;
}
else
{
2020-03-07 20:20:38 +01:00
result . real_dst_address = dst_base_address ;
2018-02-10 17:21:16 +01:00
result . real_dst_size = dst . pitch * dst_dimensions . height ;
}
return result ;
2017-09-08 16:52:13 +02:00
}
2017-09-22 22:43:01 +02:00
2018-02-03 09:37:42 +01:00
void do_update ( )
{
2019-06-07 21:56:30 +02:00
if ( ! m_flush_always_cache . empty ( ) )
2018-02-03 09:37:42 +01:00
{
2020-12-06 13:15:19 +01:00
if ( m_cache_update_tag . load ( ) ! = m_flush_always_update_timestamp )
2018-02-03 09:37:42 +01:00
{
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( m_cache_mutex ) ;
2018-04-16 00:59:45 +02:00
bool update_tag = false ;
2018-02-03 09:37:42 +01:00
for ( const auto & It : m_flush_always_cache )
{
2018-09-22 02:14:26 +02:00
auto & section = * ( It . second ) ;
2018-02-03 09:37:42 +01:00
if ( section . get_protection ( ) ! = utils : : protection : : no )
{
2020-12-09 08:47:45 +01:00
ensure ( section . exists ( ) ) ;
2018-09-22 02:14:26 +02:00
AUDIT ( section . get_context ( ) = = texture_upload_context : : framebuffer_storage ) ;
AUDIT ( section . get_memory_read_flags ( ) = = memory_read_flags : : flush_always ) ;
section . reprotect ( utils : : protection : : no ) ;
update_tag = true ;
2018-02-03 09:37:42 +01:00
}
}
2018-04-16 00:59:45 +02:00
if ( update_tag ) update_cache_tag ( ) ;
2020-12-06 13:15:19 +01:00
m_flush_always_update_timestamp = m_cache_update_tag . load ( ) ;
2018-09-22 02:14:26 +02:00
# ifdef TEXTURE_CACHE_DEBUG
// Check that the cache has the correct protections
m_storage . verify_protection ( ) ;
# endif // TEXTURE_CACHE_DEBUG
2018-02-03 09:37:42 +01:00
}
}
}
2018-10-28 14:59:39 +01:00
predictor_type & get_predictor ( )
{
return m_predictor ;
}
/**
* The read only texture invalidate flag is set if a read only texture is trampled by framebuffer memory
* If set , all cached read only textures are considered invalid and should be re - fetched from the texture cache
*/
void clear_ro_tex_invalidate_intr ( )
{
read_only_tex_invalidate = false ;
}
bool get_ro_tex_invalidate_intr ( ) const
{
return read_only_tex_invalidate ;
}
/**
* Per - frame statistics
*/
2018-02-11 13:48:36 +01:00
void reset_frame_statistics ( )
{
2018-10-28 14:59:39 +01:00
m_flushes_this_frame . store ( 0u ) ;
m_misses_this_frame . store ( 0u ) ;
m_speculations_this_frame . store ( 0u ) ;
m_unavoidable_hard_faults_this_frame . store ( 0u ) ;
2020-12-14 19:29:07 +01:00
m_texture_upload_calls_this_frame . store ( 0u ) ;
m_texture_upload_misses_this_frame . store ( 0u ) ;
2018-02-11 13:48:36 +01:00
}
2018-10-28 14:59:39 +01:00
void on_flush ( )
2017-09-22 22:43:01 +02:00
{
2018-10-28 14:59:39 +01:00
m_flushes_this_frame + + ;
2017-09-22 22:43:01 +02:00
}
2017-10-21 23:12:32 +02:00
2018-10-28 14:59:39 +01:00
void on_speculative_flush ( )
2017-10-21 23:12:32 +02:00
{
2018-10-28 14:59:39 +01:00
m_speculations_this_frame + + ;
2017-10-21 23:12:32 +02:00
}
2017-10-26 15:20:09 +02:00
2018-10-28 14:59:39 +01:00
void on_misprediction ( )
2018-02-11 13:48:36 +01:00
{
2018-10-28 14:59:39 +01:00
m_predictor . on_misprediction ( ) ;
2018-02-11 13:48:36 +01:00
}
2018-10-28 14:59:39 +01:00
void on_miss ( const section_storage_type & section )
2018-02-23 20:49:59 +01:00
{
2018-10-28 14:59:39 +01:00
m_misses_this_frame + + ;
if ( section . get_memory_read_flags ( ) = = memory_read_flags : : flush_always )
{
m_unavoidable_hard_faults_this_frame + + ;
}
2018-02-23 20:49:59 +01:00
}
2021-03-07 16:49:42 +01:00
virtual u32 get_unreleased_textures_count ( ) const
2018-06-24 12:23:30 +02:00
{
2018-10-28 14:59:39 +01:00
return m_storage . m_unreleased_texture_objects ;
2018-06-24 12:23:30 +02:00
}
2021-03-07 16:49:42 +01:00
u64 get_texture_memory_in_use ( ) const
2018-02-11 13:48:36 +01:00
{
2018-10-28 14:59:39 +01:00
return m_storage . m_texture_memory_in_use ;
2018-02-11 13:48:36 +01:00
}
2018-10-28 14:59:39 +01:00
u32 get_num_flush_requests ( ) const
2018-01-31 17:11:03 +01:00
{
2018-10-28 14:59:39 +01:00
return m_flushes_this_frame ;
2018-01-31 17:11:03 +01:00
}
2018-10-28 14:59:39 +01:00
u32 get_num_cache_mispredictions ( ) const
2018-01-31 17:11:03 +01:00
{
2018-10-28 14:59:39 +01:00
return m_predictor . m_mispredictions_this_frame ;
2018-01-31 17:11:03 +01:00
}
2018-10-28 14:59:39 +01:00
u32 get_num_cache_speculative_writes ( ) const
2017-10-26 15:20:09 +02:00
{
2018-10-28 14:59:39 +01:00
return m_speculations_this_frame ;
2017-10-26 15:20:09 +02:00
}
2018-10-28 14:59:39 +01:00
u32 get_num_cache_misses ( ) const
2017-10-26 15:20:09 +02:00
{
2018-10-28 14:59:39 +01:00
return m_misses_this_frame ;
}
u32 get_num_unavoidable_hard_faults ( ) const
{
return m_unavoidable_hard_faults_this_frame ;
}
f32 get_cache_miss_ratio ( ) const
{
const auto num_flushes = m_flushes_this_frame . load ( ) ;
2019-12-03 23:34:23 +01:00
return ( num_flushes = = 0u ) ? 0.f : static_cast < f32 > ( m_misses_this_frame . load ( ) ) / num_flushes ;
2017-10-26 15:20:09 +02:00
}
2020-12-14 19:29:07 +01:00
2021-04-09 21:12:47 +02:00
u32 get_texture_upload_calls_this_frame ( ) const
2020-12-14 19:29:07 +01:00
{
return m_texture_upload_calls_this_frame ;
}
2021-04-09 21:12:47 +02:00
u32 get_texture_upload_misses_this_frame ( ) const
2020-12-14 19:29:07 +01:00
{
return m_texture_upload_misses_this_frame ;
}
2021-04-09 21:12:47 +02:00
u32 get_texture_upload_miss_percentage ( ) const
2020-12-14 19:29:07 +01:00
{
return ( m_texture_upload_calls_this_frame ) ? ( m_texture_upload_misses_this_frame * 100 / m_texture_upload_calls_this_frame ) : 0 ;
}
2017-09-08 16:52:13 +02:00
} ;
}