2018-09-19 00:21:57 +02:00
# pragma once
2017-09-08 16:52:13 +02:00
# include "../rsx_cache.h"
# include "../rsx_utils.h"
2018-10-28 14:59:39 +01:00
# include "texture_cache_predictor.h"
2018-09-22 02:14:26 +02:00
# include "texture_cache_utils.h"
2017-10-23 18:06:54 +02:00
# include "TextureUtils.h"
2017-09-08 16:52:13 +02:00
# include <atomic>
2017-10-28 21:17:27 +02:00
extern u64 get_system_time ( ) ;
2017-09-08 16:52:13 +02:00
namespace rsx
{
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
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 ;
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
{
2018-09-22 02:14:26 +02:00
size_t flush_and_unprotect_count = sections_to_flush . size ( ) + sections_to_unprotect . size ( ) ;
size_t 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
2018-10-19 00:22:00 +02:00
ASSERT ( 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 )
ASSERT ( 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
ASSERT ( 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
ASSERT ( 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-02-02 20:44:18 +01:00
enum surface_transform : u32
{
identity = 0 ,
argb_to_bgra = 1
} ;
2018-02-21 11:46:23 +01:00
struct copy_region_descriptor
{
image_resource_type src ;
2019-02-02 20:44:18 +01:00
surface_transform xform ;
2018-02-21 11:46:23 +01:00
u16 src_x ;
u16 src_y ;
u16 dst_x ;
u16 dst_y ;
2018-03-30 12:28:46 +02:00
u16 dst_z ;
2019-02-02 20:44:18 +01:00
u16 src_w ;
u16 src_h ;
u16 dst_w ;
u16 dst_h ;
2018-02-21 11:46:23 +01:00
} ;
2018-03-30 12:28:46 +02:00
enum deferred_request_command : u32
{
2019-02-25 16:03:14 +01:00
nop = 0 ,
2018-03-30 12:28:46 +02:00
copy_image_static ,
copy_image_dynamic ,
cubemap_gather ,
cubemap_unwrap ,
atlas_gather ,
_3d_gather ,
_3d_unwrap
} ;
2018-03-18 12:40:26 +01:00
using texture_channel_remap_t = std : : pair < std : : array < u8 , 4 > , std : : array < u8 , 4 > > ;
2017-11-03 12:16:55 +01:00
struct deferred_subresource
{
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
u32 base_address = 0 ;
u32 gcm_format = 0 ;
u16 x = 0 ;
u16 y = 0 ;
u16 width = 0 ;
u16 height = 0 ;
2018-03-30 12:28:46 +02:00
u16 depth = 1 ;
2019-04-01 17:45:19 +02:00
bool do_not_cache = false ;
2017-11-03 12:16:55 +01:00
deferred_subresource ( )
{ }
2018-03-30 12:28:46 +02:00
deferred_subresource ( image_resource_type _res , deferred_request_command _op , u32 _addr , u32 _fmt , u16 _x , u16 _y , u16 _w , u16 _h , u16 _d , const texture_channel_remap_t & _remap ) :
external_handle ( _res ) , op ( _op ) , base_address ( _addr ) , gcm_format ( _fmt ) , x ( _x ) , y ( _y ) , width ( _w ) , height ( _h ) , depth ( _d ) , remap ( _remap )
2017-11-03 12:16:55 +01:00
{ }
} ;
2018-02-10 17:21:16 +01:00
struct blit_op_result
{
bool succeeded = false ;
bool is_depth = false ;
u32 real_dst_address = 0 ;
u32 real_dst_size = 0 ;
blit_op_result ( bool success ) : succeeded ( success )
{ }
2018-09-22 02:14:26 +02:00
inline address_range to_address_range ( ) const
{
return address_range : : start_length ( real_dst_address , real_dst_size ) ;
}
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 ;
sampled_image_descriptor ( )
{ }
2017-09-08 16:52:13 +02:00
2017-12-18 10:02:19 +01:00
sampled_image_descriptor ( image_view_type handle , texture_upload_context ctx , bool is_depth , f32 x_scale , f32 y_scale , rsx : : texture_dimension_extended type )
2017-11-03 12:16:55 +01:00
{
image_handle = handle ;
upload_context = ctx ;
is_depth_texture = is_depth ;
scale_x = x_scale ;
scale_y = y_scale ;
image_type = type ;
}
2018-03-30 12:28:46 +02:00
sampled_image_descriptor ( image_resource_type external_handle , deferred_request_command reason , u32 base_address , u32 gcm_format ,
u16 x_offset , u16 y_offset , u16 width , u16 height , u16 depth , texture_upload_context ctx , bool is_depth , f32 x_scale , f32 y_scale ,
rsx : : texture_dimension_extended type , const texture_channel_remap_t & remap )
2017-11-03 12:16:55 +01:00
{
2018-03-30 12:28:46 +02:00
external_subresource_desc = { external_handle , reason , base_address , gcm_format , x_offset , y_offset , width , height , depth , remap } ;
2017-11-03 12:16:55 +01:00
image_handle = 0 ;
upload_context = ctx ;
is_depth_texture = is_depth ;
scale_x = x_scale ;
scale_y = y_scale ;
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 ;
}
}
}
2019-03-04 19:22:45 +01:00
bool atlas_covers_target_area ( ) const
{
if ( external_subresource_desc . op ! = deferred_request_command : : atlas_gather )
return true ;
u16 min_x = external_subresource_desc . width , min_y = external_subresource_desc . height ,
max_x = 0 , max_y = 0 ;
2019-03-07 16:17:00 +01:00
// Require at least 50% coverage
const u32 target_area = ( min_x * min_y ) / 2 ;
2019-03-04 19:22:45 +01:00
for ( const auto & section : external_subresource_desc . sections_to_copy )
{
if ( section . dst_x < min_x ) min_x = section . dst_x ;
if ( section . dst_y < min_y ) min_y = section . dst_y ;
const auto _u = section . dst_x + section . dst_w ;
const auto _v = section . dst_y + section . dst_h ;
if ( _u > max_x ) max_x = _u ;
if ( _v > max_y ) max_y = _v ;
if ( const auto _w = max_x - min_x , _h = max_y - min_y ;
2019-03-15 16:00:12 +01:00
u32 ( _w * _h ) > = target_area )
2019-03-04 19:22:45 +01:00
{
// Target area mostly covered, return success
return true ;
}
}
return false ;
}
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 ) ;
}
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 ;
2018-10-28 14:59:39 +01:00
predictor_type m_predictor ;
2017-09-08 16:52:13 +02:00
2017-11-04 14:29:54 +01:00
std : : atomic < 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
2018-10-28 14:59:39 +01:00
std : : atomic < u32 > m_flushes_this_frame = { 0 } ;
std : : atomic < u32 > m_misses_this_frame = { 0 } ;
std : : atomic < u32 > m_speculations_this_frame = { 0 } ;
std : : atomic < u32 > m_unavoidable_hard_faults_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 ;
2018-12-18 18:04:03 +01:00
virtual section_storage_type * create_new_texture ( commandbuffer_type & , const address_range & rsx_range , u16 width , u16 height , u16 depth , u16 mipmaps , u16 pitch , u32 gcm_format ,
2018-10-28 14:59:39 +01:00
rsx : : texture_upload_context context , rsx : : texture_dimension_extended type , texture_create_flags flags ) = 0 ;
2018-12-13 11:23:58 +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 , u16 pitch , u32 gcm_format , texture_upload_context context ,
2018-10-28 14:59:39 +01:00
const std : : vector < rsx_subresource_layout > & subresource_layout , rsx : : texture_dimension_extended type , bool swizzled ) = 0 ;
2017-12-18 10:02:19 +01:00
virtual void enforce_surface_creation_type ( section_storage_type & section , u32 gcm_format , texture_create_flags expected ) = 0 ;
2018-02-01 08:44:57 +01:00
virtual void insert_texture_barrier ( commandbuffer_type & , image_storage_type * tex ) = 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 ;
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
}
2018-08-24 16:38:17 +02:00
template < typename . . . Args >
void emit_once ( bool error , const char * fmt , 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 )
2018-08-24 16:38:17 +02:00
LOG_ERROR ( RSX , " %s " , * result . first ) ;
2018-02-23 20:49:59 +01:00
else
2018-08-24 16:38:17 +02:00
LOG_WARNING ( RSX , " %s " , * result . first ) ;
2018-02-23 20:49:59 +01:00
}
2018-08-24 16:38:17 +02:00
template < typename . . . Args >
void err_once ( const char * fmt , 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
}
2018-08-24 16:38:17 +02:00
template < typename . . . Args >
void warn_once ( const char * fmt , 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 ( ) )
{
if ( other - > overlaps ( * surface , section_bounds : : full_range ) )
{
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
}
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 )
{
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
}
2018-09-22 02:14:26 +02:00
//LOG_ERROR(RSX, "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
{
verify ( HERE ) , section - > is_flushed ( ) | | section - > is_dirty ( ) ;
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
{
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 ( ) ) ;
2019-02-27 19:26:22 +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
{
fmt : : throw_exception ( " Unreachable " HERE ) ;
}
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
std : : atomic < 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
2017-09-19 14:46:16 +02:00
u32 last_dirty_block = UINT32_MAX ;
2018-08-12 01:30:23 +02:00
bool repeat_loop = false ;
2017-10-24 12:59:46 +02:00
2018-09-22 02:14:26 +02:00
// Not having full-range protections means some textures will check the confirmed range and not the locked range
const bool not_full_range_protected = ( buffered_section : : guard_policy ! = protection_policy : : protect_policy_full_range ) ;
section_bounds range_it_bounds = not_full_range_protected ? confirmed_range : locked_range ;
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02:00
auto It = m_storage . range_begin ( invalidate_range , range_it_bounds , true ) ; // will iterate through locked sections only
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...
AUDIT ( tex . cache_tag ! = cache_tag | | last_dirty_block ! = UINT32_MAX ) ; // cache tag should not match during the first loop
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 ( ) ;
if ( range_it_bounds = = 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
{
2018-09-22 02:14:26 +02:00
It = m_storage . range_begin ( invalidate_range , range_it_bounds , 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 )
{
size_t count = 0 ;
for ( auto & section2 : result . sections )
{
if ( section1 = = section2 ) count + + ;
}
verify ( HERE ) , count = = 1 ;
}
# 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
{
2019-03-02 13:14:22 +01:00
verify ( HERE ) , cause ! = invalidation_cause : : committed_as_fbo ;
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 & &
tex . get_context ( ) = = texture_upload_context : : framebuffer_storage & &
tex . get_section_base ( ) ! = fault_range_in . start )
{
// HACK: When being superseded by an fbo, we preserve other overlapped fbos unless the start addresses match
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 ) ;
}
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 ) ;
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 ;
# 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
2018-10-19 00:22:00 +02:00
if ( ! tex . is_locked ( ) )
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 ) | |
// HACK: When being superseded by an fbo, we preserve other overlapped fbos unless the start addresses match
2019-03-02 13:14:22 +01:00
// If region is committed as fbo, all non-fbo data is removed but all fbos in the region must be preserved if possible
( overlaps_fault_range & & tex . get_context ( ) = = texture_upload_context : : framebuffer_storage & & cause . skip_fbos ( ) & & ( tex . get_section_base ( ) ! = fault_range_in . start | | cause = = invalidation_cause : : committed_as_fbo ) )
2018-09-22 02:14:26 +02:00
)
{
// False positive
result . sections_to_exclude . push_back ( & tex ) ;
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
2018-09-22 02:14:26 +02:00
if ( tex . is_dirty ( ) | | ! tex . test_memory_head ( ) | | ! tex . test_memory_tail ( ) )
{
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
result . sections_to_unprotect . push_back ( & tex ) ;
continue ;
2017-11-04 14:29:54 +01:00
}
2017-10-21 23:12:32 +02:00
2018-09-22 02:14:26 +02:00
fmt : : throw_exception ( " Unreachable " HERE ) ;
}
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 ( ) ) ;
result . cache_tag = m_cache_update_tag . load ( std : : memory_order_consume ) ;
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)
2019-02-27 19:26:22 +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 ;
}
# 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
}
2018-09-22 02:14:26 +02:00
protected :
2019-03-29 20:04:54 +01:00
inline bool is_gcm_depth_format ( u32 format ) const
2017-09-19 14:46:16 +02:00
{
switch ( format )
{
case CELL_GCM_TEXTURE_DEPTH16 :
2019-03-29 20:04:54 +01:00
case CELL_GCM_TEXTURE_DEPTH16_FLOAT :
2017-09-19 14:46:16 +02:00
case CELL_GCM_TEXTURE_DEPTH24_D8 :
2019-03-29 20:04:54 +01:00
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT :
2017-09-19 14:46:16 +02:00
return true ;
default :
return false ;
}
}
2017-09-14 13:37:14 +02:00
2019-02-25 16:03:14 +01:00
inline u32 get_compatible_depth_format ( u32 gcm_format ) const
{
switch ( gcm_format )
{
case CELL_GCM_TEXTURE_DEPTH24_D8 :
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT :
case CELL_GCM_TEXTURE_A8R8G8B8 :
return CELL_GCM_TEXTURE_DEPTH24_D8 ;
case CELL_GCM_TEXTURE_DEPTH16 :
case CELL_GCM_TEXTURE_DEPTH16_FLOAT :
case CELL_GCM_TEXTURE_X16 :
2019-03-29 20:04:54 +01:00
//case CELL_GCM_TEXTURE_A4R4G4B4:
//case CELL_GCM_TEXTURE_G8B8:
//case CELL_GCM_TEXTURE_A1R5G5B5:
//case CELL_GCM_TEXTURE_R5G5B5A1:
//case CELL_GCM_TEXTURE_R5G6B5:
//case CELL_GCM_TEXTURE_R6G5B5:
2019-02-25 16:03:14 +01:00
return CELL_GCM_TEXTURE_DEPTH16 ;
}
LOG_ERROR ( RSX , " Unsupported depth conversion (0x%X) " , gcm_format ) ;
return gcm_format ;
}
inline bool is_compressed_gcm_format ( u32 format )
{
switch ( format )
{
default :
return false ;
case CELL_GCM_TEXTURE_COMPRESSED_DXT1 :
case CELL_GCM_TEXTURE_COMPRESSED_DXT23 :
case CELL_GCM_TEXTURE_COMPRESSED_DXT45 :
2019-03-30 12:33:14 +01:00
case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8 :
case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8 :
case CELL_GCM_TEXTURE_COMPRESSED_HILO8 :
case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8 :
2019-02-25 16:03:14 +01:00
return true ;
}
}
2017-10-31 23:36:39 +01:00
/**
* Scaling helpers
* - get_native_dimensions ( ) returns w and h for the native texture given rsx dimensions
* on rsx a 512 x512 texture with 4 x AA is treated as a 1024 x1024 texture for example
* - get_rsx_dimensions ( ) inverse , return rsx w and h given a real texture w and h
* - get_internal_scaling_x / y ( ) returns a scaling factor to be multiplied by 1 / size
* when sampling with unnormalized coordinates . tcoords passed to rsx will be in rsx dimensions
*/
template < typename T , typename U >
2017-11-02 16:54:57 +01:00
inline void get_native_dimensions ( T & width , T & height , U surface )
2017-10-31 23:36:39 +01:00
{
2018-06-03 13:52:21 +02:00
switch ( surface - > read_aa_mode )
2017-10-31 23:36:39 +01:00
{
case rsx : : surface_antialiasing : : center_1_sample :
return ;
case rsx : : surface_antialiasing : : diagonal_centered_2_samples :
width / = 2 ;
return ;
case rsx : : surface_antialiasing : : square_centered_4_samples :
case rsx : : surface_antialiasing : : square_rotated_4_samples :
width / = 2 ;
height / = 2 ;
return ;
}
}
template < typename T , typename U >
2017-11-02 16:54:57 +01:00
inline void get_rsx_dimensions ( T & width , T & height , U surface )
2017-10-31 23:36:39 +01:00
{
2018-06-03 13:52:21 +02:00
switch ( surface - > read_aa_mode )
2017-10-31 23:36:39 +01:00
{
case rsx : : surface_antialiasing : : center_1_sample :
return ;
case rsx : : surface_antialiasing : : diagonal_centered_2_samples :
width * = 2 ;
return ;
case rsx : : surface_antialiasing : : square_centered_4_samples :
case rsx : : surface_antialiasing : : square_rotated_4_samples :
width * = 2 ;
height * = 2 ;
return ;
}
}
template < typename T >
inline f32 get_internal_scaling_x ( T surface )
{
2018-06-03 13:52:21 +02:00
switch ( surface - > read_aa_mode )
2017-10-31 23:36:39 +01:00
{
default :
case rsx : : surface_antialiasing : : center_1_sample :
return 1.f ;
case rsx : : surface_antialiasing : : diagonal_centered_2_samples :
case rsx : : surface_antialiasing : : square_centered_4_samples :
case rsx : : surface_antialiasing : : square_rotated_4_samples :
return 0.5f ;
}
}
template < typename T >
inline f32 get_internal_scaling_y ( T surface )
{
2018-06-03 13:52:21 +02:00
switch ( surface - > read_aa_mode )
2017-10-31 23:36:39 +01:00
{
default :
case rsx : : surface_antialiasing : : center_1_sample :
case rsx : : surface_antialiasing : : diagonal_centered_2_samples :
return 1.f ;
case rsx : : surface_antialiasing : : square_centered_4_samples :
case rsx : : surface_antialiasing : : square_rotated_4_samples :
return 0.5f ;
}
}
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 ) { }
2017-09-08 16:52:13 +02:00
~ texture_cache ( ) { }
2017-11-02 07:29:17 +01:00
2018-09-22 02:14:26 +02:00
void clear ( )
{
m_storage . clear ( ) ;
2018-10-28 14:59:39 +01:00
m_predictor . clear ( ) ;
}
virtual void on_frame_end ( )
{
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 >
2019-03-02 15:40:16 +01:00
std : : vector < section_storage_type * > find_texture_from_range ( const address_range & test_range , u16 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
for ( auto It = m_storage . range_begin ( test_range , full_range ) ; 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 ;
2017-09-14 13:37:14 +02:00
2019-02-02 20:44:18 +01:00
if ( ! tex . is_dirty ( ) & & ( context_mask & ( u32 ) tex . get_context ( ) ) )
{
2019-02-25 16:03:14 +01:00
if constexpr ( check_unlocked )
{
if ( ! tex . is_locked ( ) )
continue ;
}
2019-03-02 15:40:16 +01:00
if ( required_pitch & & ! rsx : : pitch_compatible < false > ( & tex , required_pitch , UINT16_MAX ) )
2019-02-25 16:03:14 +01:00
{
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 ( ) )
continue ;
}
2019-03-26 18:59:41 +01:00
if ( ! tex . is_dirty ( ) & & tex . matches ( rsx_address , format , width , height , depth , mipmaps ) )
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 ;
}
2018-09-22 02:14:26 +02:00
section_storage_type * find_cached_texture ( const address_range & range , bool create_if_not_found , bool confirm_dimensions , 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 ( 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
{
2018-09-22 02:14:26 +02:00
if ( ! tex . is_dirty ( ) )
2017-09-08 16:52:13 +02:00
{
2018-09-22 02:14:26 +02:00
if ( ! confirm_dimensions | | tex . matches_dimensions ( width , height , depth , mipmaps ) )
2017-09-19 14:46:16 +02:00
{
2018-09-22 02:14:26 +02:00
# ifndef TEXTURE_CACHE_DEBUG
return & tex ;
# else
ASSERT ( res = = nullptr ) ;
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 ( ) )
{
//By grabbing a ref to a matching entry, duplicates are avoided
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 ;
2018-09-22 02:14:26 +02:00
LOG_WARNING ( RSX , " 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) " ,
range . start , width , tex . get_width ( ) , height , tex . get_height ( ) , depth , tex . get_depth ( ) , mipmaps , tex . get_mipmaps ( ) ) ;
}
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 >
2019-03-16 10:14:11 +01:00
void lock_memory_region ( commandbuffer_type & cmd , image_storage_type * image , const address_range & rsx_range , u32 width , u32 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
2018-11-01 02:31:12 +01:00
section_storage_type & region = * find_cached_texture ( rsx_range , true , true , width , height ) ;
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
ASSERT ( region . matches ( rsx_range ) ) ;
2018-09-22 02:14:26 +02:00
ASSERT ( region . get_context ( ) = = texture_upload_context : : framebuffer_storage ) ;
ASSERT ( 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
2018-09-22 02:14:26 +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-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 )
{
AUDIT ( ! g_cfg . video . write_color_buffers & & ! g_cfg . video . write_depth_buffer ) ;
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
}
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
2018-09-22 02:14:26 +02:00
auto * region_ptr = find_cached_texture ( memory_range , false , false ) ;
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 ( ) ) ;
LOG_ERROR ( RSX , " 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
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 )
verify ( HERE ) , m_flush_always_cache . find ( memory_range ) = = m_flush_always_cache . end ( ) ;
else
verify ( HERE ) , m_flush_always_cache [ memory_range ] = = & region ;
}
# 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 )
verify ( HERE ) , m_flush_always_cache [ memory_range ] = = & section ;
else
verify ( HERE ) , m_flush_always_cache . find ( memory_range ) = = m_flush_always_cache . end ( ) ;
# 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-09-22 02:14:26 +02:00
bool load_memory_from_cache ( const address_range & memory_range , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
2017-09-14 13:37:14 +02:00
reader_lock lock ( m_cache_mutex ) ;
2018-09-22 02:14:26 +02:00
section_storage_type * region = find_flushable_section ( memory_range ) ;
2017-09-08 16:52:13 +02:00
if ( region & & ! region - > is_dirty ( ) )
{
region - > fill_texture ( std : : forward < Args > ( extras ) . . . ) ;
return true ;
}
//No valid object found in cache
return false ;
}
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 ) ;
2017-12-04 17:30:09 +01:00
if ( m_cache_update_tag . load ( std : : memory_order_consume ) = = 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
}
2017-11-03 12:16:55 +01:00
image_view_type create_temporary_subresource ( commandbuffer_type & cmd , deferred_subresource & desc )
{
2019-04-01 17:45:19 +02:00
if ( ! desc . do_not_cache )
{
const auto found = m_temporary_subresource_cache . equal_range ( desc . base_address ) ;
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
}
image_view_type result = 0 ;
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 ,
surface_transform : : identity ,
0 , ( u16 ) ( desc . height * n ) ,
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 ,
surface_transform : : identity ,
0 , ( u16 ) ( desc . height * n ) ,
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 ;
}
default :
{
//Throw
fmt : : throw_exception ( " Invalid deferred command op 0x%X " HERE , ( u32 ) desc . op ) ;
}
}
2017-11-03 12:16:55 +01:00
if ( result )
{
m_temporary_subresource_cache . insert ( { desc . base_address , { desc , result } } ) ;
}
return result ;
}
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 ( ) ; )
{
if ( range . overlaps ( It - > first ) )
{
It = m_temporary_subresource_cache . erase ( It ) ;
}
else
{
+ + It ;
}
}
2017-11-03 12:16:55 +01:00
}
2019-02-25 16:03:14 +01:00
template < typename surface_store_list_type >
std : : vector < copy_region_descriptor > gather_texture_slices ( commandbuffer_type & cmd ,
const surface_store_list_type & fbos , const std : : vector < section_storage_type * > & local ,
u32 texaddr , u16 slice_w , u16 slice_h , u16 src_padding , u16 pitch , u16 count , u8 bpp , bool is_depth )
2018-03-30 12:28:46 +02:00
{
2019-02-25 16:03:14 +01:00
// Need to preserve sorting order
struct sort_helper
{
u64 tag ; // Timestamp
u32 list ; // List source, 0 = fbo, 1 = local
u32 index ; // Index in list
} ;
2018-03-30 12:28:46 +02:00
std : : vector < copy_region_descriptor > surfaces ;
2019-02-25 16:03:14 +01:00
std : : vector < sort_helper > sort_list ;
const u16 src_slice_h = slice_h + src_padding ;
2018-03-30 12:28:46 +02:00
2019-02-25 16:03:14 +01:00
if ( ! fbos . empty ( ) & & ! local . empty ( ) )
2018-03-30 12:28:46 +02:00
{
2019-02-25 16:03:14 +01:00
// Generate sorting tree if both resources are available and overlapping
sort_list . reserve ( fbos . size ( ) + local . size ( ) ) ;
2018-03-30 12:28:46 +02:00
2019-02-25 16:03:14 +01:00
for ( u32 index = 0 ; index < fbos . size ( ) ; + + index )
2018-03-30 12:28:46 +02:00
{
2019-02-25 16:03:14 +01:00
sort_list . push_back ( { fbos [ index ] . surface - > last_use_tag , 0 , index } ) ;
2018-03-30 12:28:46 +02:00
}
2019-02-25 16:03:14 +01:00
for ( u32 index = 0 ; index < local . size ( ) ; + + index )
2018-03-30 12:28:46 +02:00
{
2019-03-02 15:40:16 +01:00
if ( local [ index ] - > get_context ( ) ! = rsx : : texture_upload_context : : blit_engine_dst )
2019-02-25 16:03:14 +01:00
continue ;
sort_list . push_back ( { local [ index ] - > last_write_tag , 1 , index } ) ;
2018-03-30 12:28:46 +02:00
}
2019-02-25 16:03:14 +01:00
std : : sort ( sort_list . begin ( ) , sort_list . end ( ) , [ ] ( const auto & a , const auto & b )
{
return ( a . tag < b . tag ) ;
} ) ;
2018-03-30 12:28:46 +02:00
}
2019-02-25 16:03:14 +01:00
auto add_rtt_resource = [ & ] ( auto & section , u16 slice )
2018-03-30 12:28:46 +02:00
{
2019-02-25 16:03:14 +01:00
if ( section . is_depth ! = is_depth )
{
// TODO
return ;
}
2018-03-30 12:28:46 +02:00
2019-02-25 16:03:14 +01:00
const auto slice_begin = ( slice * src_slice_h ) ;
const auto slice_end = ( slice_begin + slice_h ) ;
2018-03-30 12:28:46 +02:00
2019-02-25 16:03:14 +01:00
const auto section_end = section . dst_y + section . height ;
if ( section . dst_y > = slice_end | | section_end < = slice_begin )
{
// Belongs to a different slice
return ;
}
2017-11-02 19:54:19 +01:00
2019-02-25 16:03:14 +01:00
section . surface - > read_barrier ( cmd ) ;
2018-03-30 12:28:46 +02:00
2019-02-25 16:03:14 +01:00
// How much of this slice to read?
int rebased = int ( section . dst_y ) - slice_begin ;
const auto src_x = section . src_x ;
const auto dst_x = section . dst_x ;
auto src_y = section . src_y ;
auto dst_y = section . dst_y ;
2018-12-16 12:57:22 +01:00
2019-02-25 16:03:14 +01:00
if ( rebased < 0 )
2017-11-02 19:54:19 +01:00
{
2019-02-25 16:03:14 +01:00
const u16 delta = u16 ( - rebased ) ;
src_y + = delta ;
dst_y + = delta ;
}
2017-11-02 19:54:19 +01:00
2019-02-25 16:03:14 +01:00
verify ( HERE ) , dst_y > = slice_begin ;
dst_y = ( dst_y - slice_begin ) ;
2017-11-02 19:54:19 +01:00
2019-02-25 16:03:14 +01:00
const auto scale_x = 1.f / get_internal_scaling_x ( section . surface ) ;
const auto scale_y = 1.f / get_internal_scaling_y ( section . surface ) ;
const auto h = std : : min ( section_end , slice_end ) - section . dst_y ;
auto src_width = rsx : : apply_resolution_scale ( section . width , true ) ;
auto src_height = rsx : : apply_resolution_scale ( h , true ) ;
2019-02-26 11:31:32 +01:00
auto dst_width = u16 ( src_width * scale_x ) ;
auto dst_height = u16 ( src_height * scale_y ) ;
2019-02-25 16:03:14 +01:00
if ( scale_x > 1.f )
2018-03-30 12:28:46 +02:00
{
2019-02-25 16:03:14 +01:00
// Clipping
const auto limit_x = dst_x + dst_width ;
const auto limit_y = dst_x + dst_height ;
if ( limit_x > slice_w )
2017-11-02 19:54:19 +01:00
{
2019-02-26 11:31:32 +01:00
dst_width = ( slice_w - dst_x ) ;
2019-02-27 19:26:22 +01:00
src_width = u16 ( dst_width / scale_x ) ;
2017-11-02 19:54:19 +01:00
}
2017-11-03 13:48:27 +01:00
2019-02-25 16:03:14 +01:00
if ( limit_y > slice_h )
{
2019-02-26 11:31:32 +01:00
dst_height = ( slice_h - dst_y ) ;
2019-02-27 19:26:22 +01:00
src_height = u16 ( dst_height / scale_y ) ;
2019-02-25 16:03:14 +01:00
}
2017-11-02 19:54:19 +01:00
}
2017-11-02 16:54:57 +01:00
2019-02-25 16:03:14 +01:00
surfaces . push_back
( {
section . surface - > get_surface ( ) ,
surface_transform : : identity ,
rsx : : apply_resolution_scale ( src_x , true ) ,
rsx : : apply_resolution_scale ( src_y , true ) ,
rsx : : apply_resolution_scale ( dst_x , true ) ,
rsx : : apply_resolution_scale ( dst_y , true ) ,
slice ,
src_width , src_height ,
dst_width , dst_height
} ) ;
} ;
2018-02-21 11:46:23 +01:00
2019-02-25 16:03:14 +01:00
auto add_local_resource = [ & ] ( auto & section , u32 address , u16 slice , bool scaling = true )
2018-08-14 21:58:50 +02:00
{
2019-02-25 16:03:14 +01:00
if ( section - > is_depth_texture ( ) ! = is_depth )
{
// TODO
return ;
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
// Intersect this resource with the original one
const auto section_bpp = get_format_block_size_in_bytes ( section - > get_gcm_format ( ) ) ;
2019-03-29 20:04:54 +01:00
const auto normalized_width = ( section - > get_width ( ) * section_bpp ) / bpp ;
2019-02-25 16:03:14 +01:00
const auto clipped = rsx : : intersect_region ( address , slice_w , slice_h , bpp ,
2019-03-29 20:04:54 +01:00
section - > get_section_base ( ) , normalized_width , section - > get_height ( ) , section_bpp , pitch ) ;
2019-02-02 20:44:18 +01:00
2019-03-21 10:48:20 +01:00
const auto slice_begin = u32 ( slice * src_slice_h ) ;
const auto slice_end = u32 ( slice_begin + slice_h ) ;
2019-03-16 12:31:39 +01:00
const auto dst_y = std : : get < 1 > ( clipped ) . y ;
const auto dst_h = std : : get < 2 > ( clipped ) . height ;
const auto section_end = dst_y + dst_h ;
if ( dst_y > = slice_end | | section_end < = slice_begin )
{
// Belongs to a different slice
return ;
}
2019-03-29 20:04:54 +01:00
const u16 internal_clip_width = u16 ( std : : get < 2 > ( clipped ) . width * bpp ) / section_bpp ;
2019-02-25 16:03:14 +01:00
if ( scaling )
2018-08-14 21:58:50 +02:00
{
2019-02-25 16:03:14 +01:00
// Since output is upscaled, also upscale on dst
surfaces . push_back
( {
section - > get_raw_texture ( ) ,
is_depth ? surface_transform : : identity : surface_transform : : argb_to_bgra ,
( u16 ) std : : get < 0 > ( clipped ) . x ,
( u16 ) std : : get < 0 > ( clipped ) . y ,
rsx : : apply_resolution_scale ( ( u16 ) std : : get < 1 > ( clipped ) . x , true ) ,
rsx : : apply_resolution_scale ( ( u16 ) std : : get < 1 > ( clipped ) . y , true ) ,
slice ,
2019-03-29 20:04:54 +01:00
internal_clip_width ,
2019-02-25 16:03:14 +01:00
( u16 ) std : : get < 2 > ( clipped ) . height ,
2019-03-29 20:04:54 +01:00
rsx : : apply_resolution_scale ( internal_clip_width , true ) ,
2019-02-25 16:03:14 +01:00
rsx : : apply_resolution_scale ( ( u16 ) std : : get < 2 > ( clipped ) . height , true ) ,
} ) ;
2018-08-14 21:58:50 +02:00
}
else
{
2019-03-29 20:04:54 +01:00
const auto src_width = internal_clip_width , dst_width = src_width ;
2019-02-25 16:03:14 +01:00
const auto src_height = ( u16 ) std : : get < 2 > ( clipped ) . height , dst_height = src_height ;
surfaces . push_back
( {
section - > get_raw_texture ( ) ,
is_depth ? surface_transform : : identity : surface_transform : : argb_to_bgra ,
( u16 ) std : : get < 0 > ( clipped ) . x ,
( u16 ) std : : get < 0 > ( clipped ) . y ,
( u16 ) std : : get < 1 > ( clipped ) . x ,
( u16 ) std : : get < 1 > ( clipped ) . y ,
0 ,
src_width ,
src_height ,
dst_width ,
dst_height ,
} ) ;
}
} ;
2018-08-14 21:58:50 +02:00
2019-02-25 16:03:14 +01:00
u32 current_address = texaddr ;
u16 current_src_offset = 0 ;
u16 current_dst_offset = 0 ;
u32 slice_size = ( pitch * src_slice_h ) ;
2018-02-21 11:46:23 +01:00
2019-02-25 16:03:14 +01:00
surfaces . reserve ( count ) ;
u16 found_slices = 0 ;
2018-02-21 11:46:23 +01:00
2019-02-25 16:03:14 +01:00
for ( u16 slice = 0 ; slice < count ; + + slice )
{
auto num_surface = surfaces . size ( ) ;
2018-08-01 22:55:14 +02:00
2019-02-25 16:03:14 +01:00
if ( LIKELY ( local . empty ( ) ) )
{
for ( auto & section : fbos )
2019-02-02 20:44:18 +01:00
{
2019-02-25 16:03:14 +01:00
add_rtt_resource ( section , slice ) ;
}
}
else if ( fbos . empty ( ) )
{
for ( auto & section : local )
{
add_local_resource ( section , current_address , slice , false ) ;
}
}
else
{
for ( const auto & e : sort_list )
2019-02-02 20:44:18 +01:00
{
2019-02-25 16:03:14 +01:00
if ( e . list = = 0 )
2019-02-02 20:44:18 +01:00
{
2019-02-25 16:03:14 +01:00
add_rtt_resource ( fbos [ e . index ] , slice ) ;
2019-02-02 20:44:18 +01:00
}
2019-02-25 16:03:14 +01:00
else
2019-02-02 20:44:18 +01:00
{
2019-02-25 16:03:14 +01:00
add_local_resource ( local [ e . index ] , current_address , slice ) ;
}
}
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
current_address + = slice_size ;
if ( surfaces . size ( ) ! = num_surface )
{
found_slices + + ;
}
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
if ( found_slices < count )
{
2019-02-28 11:42:51 +01:00
if ( found_slices > 0 )
{
//TODO: Gather remaining sides from the texture cache or upload from cpu (too slow?)
LOG_ERROR ( RSX , " Could not gather all required slices for cubemap/3d generation " ) ;
}
else
{
LOG_WARNING ( RSX , " Could not gather textures into an atlas; using CPU fallback... " ) ;
}
2019-02-25 16:03:14 +01:00
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
return surfaces ;
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
template < typename render_target_type >
bool check_framebuffer_resource ( commandbuffer_type & cmd , render_target_type texptr ,
u16 tex_width , u16 tex_height , u16 tex_depth , u16 tex_pitch ,
rsx : : texture_dimension_extended extended_dimension )
{
2019-03-02 15:40:16 +01:00
if ( ! rsx : : pitch_compatible ( texptr , tex_pitch , tex_height ) )
2019-02-25 16:03:14 +01:00
{
return false ;
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
const auto surface_width = texptr - > get_surface_width ( ) ;
const auto surface_height = texptr - > get_surface_height ( ) ;
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
u32 internal_width = tex_width ;
u32 internal_height = tex_height ;
get_native_dimensions ( internal_width , internal_height , texptr ) ;
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
switch ( extended_dimension )
{
case rsx : : texture_dimension_extended : : texture_dimension_1d :
return ( surface_width > = internal_width ) ;
case rsx : : texture_dimension_extended : : texture_dimension_2d :
return ( surface_width > = internal_width & & surface_height > = internal_height ) ;
case rsx : : texture_dimension_extended : : texture_dimension_3d :
return ( surface_width > = internal_width & & surface_height > = ( internal_height * tex_depth ) ) ;
case rsx : : texture_dimension_extended : : texture_dimension_cubemap :
return ( surface_width = = internal_height & & surface_width > = internal_width & & surface_height > = ( internal_height * 6 ) ) ;
}
2019-02-02 20:44:18 +01:00
2019-02-25 16:03:14 +01:00
return false ;
}
template < typename render_target_type >
sampled_image_descriptor process_framebuffer_resource_fast ( commandbuffer_type & cmd , render_target_type texptr ,
2019-03-11 21:18:22 +01:00
u32 texaddr , u32 format ,
u16 tex_width , u16 tex_height , u16 tex_depth ,
f32 scale_x , f32 scale_y ,
rsx : : texture_dimension_extended extended_dimension ,
u32 encoded_remap , const texture_channel_remap_t & decoded_remap ,
2019-02-25 16:03:14 +01:00
bool assume_bound = true )
{
texptr - > read_barrier ( cmd ) ;
const auto surface_width = texptr - > get_surface_width ( ) ;
const auto surface_height = texptr - > get_surface_height ( ) ;
u32 internal_width = tex_width ;
u32 internal_height = tex_height ;
get_native_dimensions ( internal_width , internal_height , texptr ) ;
2019-03-29 20:04:54 +01:00
bool is_depth = texptr - > is_depth_surface ( ) ;
const bool force_convert = ! render_target_format_is_compatible ( texptr , format ) ;
if ( const bool gcm_format_is_depth = is_gcm_depth_format ( format ) ;
gcm_format_is_depth ! = is_depth )
{
if ( force_convert )
{
is_depth = gcm_format_is_depth ;
}
else
{
format = get_compatible_depth_format ( format ) ;
}
// Always make sure the conflict is resolved!
verify ( HERE ) , is_gcm_depth_format ( format ) = = is_depth ;
}
2019-02-25 16:03:14 +01:00
if ( LIKELY ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_2d | |
extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d ) )
{
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d )
{
internal_height = 1 ;
}
if ( ( assume_bound & & g_cfg . video . strict_rendering_mode ) | |
internal_width < surface_width | |
internal_height < surface_height | |
2019-03-29 20:04:54 +01:00
force_convert )
2019-02-25 16:03:14 +01:00
{
const auto scaled_w = rsx : : apply_resolution_scale ( internal_width , true ) ;
const auto scaled_h = rsx : : apply_resolution_scale ( internal_height , true ) ;
2019-03-29 20:04:54 +01:00
const auto command = assume_bound ? deferred_request_command : : copy_image_dynamic : deferred_request_command : : copy_image_static ;
2019-02-25 16:03:14 +01:00
return { texptr - > get_surface ( ) , command , texaddr , format , 0 , 0 , scaled_w , scaled_h , 1 ,
texture_upload_context : : framebuffer_storage , is_depth , scale_x , scale_y ,
extended_dimension , decoded_remap } ;
}
if ( assume_bound )
{
insert_texture_barrier ( cmd , texptr ) ;
2019-02-02 20:44:18 +01:00
}
2019-02-25 16:03:14 +01:00
return { texptr - > get_view ( encoded_remap , decoded_remap ) , texture_upload_context : : framebuffer_storage ,
is_depth , scale_x , scale_y , rsx : : texture_dimension_extended : : texture_dimension_2d } ;
2018-02-21 11:46:23 +01:00
}
2019-02-25 16:03:14 +01:00
const auto scaled_w = rsx : : apply_resolution_scale ( internal_width , true ) ;
const auto scaled_h = rsx : : apply_resolution_scale ( internal_height , true ) ;
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_3d )
2017-11-02 16:54:57 +01:00
{
2019-02-25 16:03:14 +01:00
return { texptr - > get_surface ( ) , deferred_request_command : : _3d_unwrap , texaddr , format , 0 , 0 ,
scaled_w , scaled_h , tex_depth ,
texture_upload_context : : framebuffer_storage , is_depth , 1.f , 1.f ,
rsx : : texture_dimension_extended : : texture_dimension_3d , decoded_remap } ;
}
verify ( HERE ) , extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_cubemap ;
return { texptr - > get_surface ( ) , deferred_request_command : : cubemap_unwrap , texaddr , format , 0 , 0 ,
scaled_w , scaled_h , 1 ,
texture_upload_context : : framebuffer_storage , is_depth , 1.f , 1.f ,
rsx : : texture_dimension_extended : : texture_dimension_cubemap , decoded_remap } ;
}
2018-02-20 14:29:03 +01:00
2019-02-25 16:03:14 +01:00
template < typename surface_store_list_type >
sampled_image_descriptor merge_cache_resources ( commandbuffer_type & cmd , const surface_store_list_type & fbos , const std : : vector < section_storage_type * > & local ,
2019-03-11 21:18:22 +01:00
u32 texaddr , u32 format ,
u16 tex_width , u16 tex_height , u16 tex_depth , u16 tex_pitch , u16 slice_h ,
f32 scale_x , f32 scale_y ,
rsx : : texture_dimension_extended extended_dimension ,
u32 encoded_remap , const texture_channel_remap_t & decoded_remap ,
int select_hint = - 1 )
2019-02-25 16:03:14 +01:00
{
2019-02-28 11:42:51 +01:00
verify ( HERE ) , ( select_hint & 0x1 ) = = select_hint ;
2019-03-11 21:18:22 +01:00
bool is_depth ;
2019-02-28 11:42:51 +01:00
if ( is_depth = ( select_hint = = 0 ) ? fbos . back ( ) . is_depth : local . back ( ) - > is_depth_texture ( ) ;
is_depth )
2019-02-25 16:03:14 +01:00
{
2019-03-29 20:04:54 +01:00
if ( const auto suggested_format = get_compatible_depth_format ( format ) ;
! is_gcm_depth_format ( suggested_format ) )
{
// Failed!
is_depth = false ;
}
else
{
format = suggested_format ;
}
2017-11-02 16:54:57 +01:00
}
2019-02-25 16:03:14 +01:00
// If this method was called, there is no easy solution, likely means atlas gather is needed
auto scaled_w = rsx : : apply_resolution_scale ( tex_width , true ) ;
auto scaled_h = rsx : : apply_resolution_scale ( tex_height , true ) ;
2019-02-28 11:42:51 +01:00
const auto bpp = get_format_block_size_in_bytes ( format ) ;
2019-02-25 16:03:14 +01:00
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_cubemap )
2018-02-23 20:49:59 +01:00
{
2019-02-25 16:03:14 +01:00
sampled_image_descriptor desc = { nullptr , deferred_request_command : : cubemap_gather , texaddr , format , 0 , 0 ,
scaled_w , scaled_w , 1 ,
texture_upload_context : : framebuffer_storage , is_depth , 1.f , 1.f ,
rsx : : texture_dimension_extended : : texture_dimension_cubemap , decoded_remap } ;
2018-02-23 20:49:59 +01:00
2019-02-25 16:03:14 +01:00
u16 padding = u16 ( slice_h - tex_width ) ;
desc . external_subresource_desc . sections_to_copy = std : : move ( gather_texture_slices ( cmd , fbos , local , texaddr , tex_width , tex_height , padding , tex_pitch , 6 , bpp , is_depth ) ) ;
return desc ;
}
else if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_3d & & tex_depth > 1 )
2017-11-02 16:54:57 +01:00
{
2019-02-25 16:03:14 +01:00
sampled_image_descriptor desc = { nullptr , deferred_request_command : : _3d_gather , texaddr , format , 0 , 0 ,
scaled_w , scaled_h , tex_depth ,
texture_upload_context : : framebuffer_storage , is_depth , 1.f , 1.f ,
rsx : : texture_dimension_extended : : texture_dimension_3d , decoded_remap } ;
2018-02-21 11:46:23 +01:00
2019-02-25 16:03:14 +01:00
u16 padding = u16 ( slice_h - tex_height ) ;
2019-03-07 16:17:00 +01:00
desc . external_subresource_desc . sections_to_copy = std : : move ( gather_texture_slices ( cmd , fbos , local , texaddr , tex_width , tex_height , padding , tex_pitch , tex_depth , bpp , is_depth ) ) ;
2019-02-25 16:03:14 +01:00
return desc ;
2017-11-02 16:54:57 +01:00
}
2019-02-25 16:03:14 +01:00
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d )
{
verify ( HERE ) , tex_height = = 1 ;
}
const auto w = fbos . empty ( ) ? tex_width : rsx : : apply_resolution_scale ( tex_width , true ) ;
const auto h = fbos . empty ( ) ? tex_height : rsx : : apply_resolution_scale ( tex_height , true ) ;
sampled_image_descriptor result = { nullptr , deferred_request_command : : atlas_gather ,
texaddr , format , 0 , 0 , w , h , 1 , texture_upload_context : : framebuffer_storage , is_depth ,
scale_x , scale_y , rsx : : texture_dimension_extended : : texture_dimension_2d , decoded_remap } ;
result . external_subresource_desc . sections_to_copy = gather_texture_slices ( cmd , fbos , local , texaddr , tex_width , tex_height , 0 , tex_pitch , 1 , bpp , is_depth ) ;
result . simplify ( ) ;
return result ;
2017-11-02 16:54:57 +01:00
}
2017-10-23 17:17:54 +02:00
template < typename RsxTextureType , typename surface_store_type , typename . . . Args >
2017-10-30 13:27:22 +01:00
sampled_image_descriptor upload_texture ( commandbuffer_type & cmd , RsxTextureType & tex , surface_store_type & m_rtts , Args & & . . . extras )
2017-09-08 16:52:13 +02:00
{
const u32 texaddr = rsx : : get_address ( tex . offset ( ) , tex . location ( ) ) ;
2018-12-13 11:23:58 +01:00
const u32 tex_size = ( u32 ) get_texture_size ( tex ) ;
2018-09-22 02:14:26 +02:00
const address_range tex_range = address_range : : start_length ( texaddr , tex_size ) ;
2017-09-08 16:52:13 +02:00
const u32 format = tex . format ( ) & ~ ( CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN ) ;
2019-02-25 16:03:14 +01:00
const bool is_compressed_format = is_compressed_gcm_format ( format ) ;
2019-03-07 16:17:00 +01:00
const bool unnormalized = ( tex . format ( ) & CELL_GCM_TEXTURE_UN ) ! = 0 ;
2017-09-08 16:52:13 +02:00
2017-09-22 15:12:10 +02:00
const auto extended_dimension = tex . get_extended_texture_dimension ( ) ;
2019-03-11 21:18:22 +01:00
u16 tex_width = tex . width ( ) ;
2018-12-13 11:23:58 +01:00
u16 tex_height = tex . height ( ) ;
2019-02-25 16:03:14 +01:00
u16 tex_pitch = ( tex . format ( ) & CELL_GCM_TEXTURE_LN ) ? ( u16 ) tex . pitch ( ) : get_format_packed_pitch ( format , tex_width ) ;
2017-09-22 15:12:10 +02:00
2018-12-13 11:23:58 +01:00
u16 depth ;
2017-11-02 16:54:57 +01:00
switch ( extended_dimension )
{
case rsx : : texture_dimension_extended : : texture_dimension_1d :
depth = 1 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_2d :
depth = 1 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_cubemap :
depth = 6 ;
break ;
case rsx : : texture_dimension_extended : : texture_dimension_3d :
depth = tex . depth ( ) ;
break ;
}
2019-03-11 21:18:22 +01:00
f32 scale_x = ( unnormalized ) ? ( 1.f / tex_width ) : 1.f ;
f32 scale_y = ( unnormalized ) ? ( 1.f / tex_height ) : 1.f ;
if ( ! tex_pitch )
{
// Linear scanning with pitch of 0, read only texel (0,0)
tex_pitch = get_format_packed_pitch ( format , tex_width ) ;
scale_x = 0.f ;
scale_y = 0.f ;
}
else if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d )
{
scale_y = 0.f ;
}
2019-03-07 16:17:00 +01:00
// Sanity check
if ( UNLIKELY ( unnormalized & & extended_dimension > rsx : : texture_dimension_extended : : texture_dimension_2d ) )
{
LOG_ERROR ( RSX , " Unimplemented unnormalized sampling for texture type %d " , ( u32 ) extended_dimension ) ;
}
2019-02-25 16:03:14 +01:00
if ( UNLIKELY ( m_rtts . address_is_bound ( texaddr ) ) )
2017-09-08 16:52:13 +02:00
{
2019-02-25 16:03:14 +01:00
if ( auto texptr = m_rtts . get_surface_at ( texaddr ) ;
check_framebuffer_resource ( cmd , texptr , tex_width , tex_height , depth , tex_pitch , extended_dimension ) )
2017-10-23 17:17:54 +02:00
{
2019-03-11 21:18:22 +01:00
return process_framebuffer_resource_fast ( cmd , texptr , texaddr , format , tex_width , tex_height , depth ,
scale_x , scale_y , extended_dimension , tex . remap ( ) , tex . decoded_remap ( ) ) ;
2017-09-08 16:52:13 +02:00
}
}
2019-02-25 16:03:14 +01:00
// Check shader_read storage. In a given scene, reads from local memory far outnumber reads from the surface cache
2019-03-16 12:31:39 +01:00
const u32 lookup_mask = ( is_compressed_format ) ? rsx : : texture_upload_context : : shader_read :
2019-02-25 16:03:14 +01:00
rsx : : texture_upload_context : : shader_read | rsx : : texture_upload_context : : blit_engine_dst | rsx : : texture_upload_context : : blit_engine_src ;
2017-11-05 15:57:00 +01:00
2019-03-16 12:31:39 +01:00
auto lookup_range = tex_range ;
if ( LIKELY ( extended_dimension < = rsx : : texture_dimension_extended : : texture_dimension_2d ) )
{
// Optimize the range a bit by only searching for mip0, layer0 to avoid false positives
const auto texel_rows_per_line = get_format_texel_rows_per_line ( format ) ;
const auto num_rows = ( tex_height + texel_rows_per_line - 1 ) / texel_rows_per_line ;
2019-03-21 10:48:20 +01:00
if ( const auto length = u32 ( num_rows * tex_pitch ) ; length < tex_range . length ( ) )
2019-03-16 12:31:39 +01:00
{
lookup_range = utils : : address_range : : start_length ( texaddr , length ) ;
}
}
reader_lock lock ( m_cache_mutex ) ;
const auto overlapping_locals = find_texture_from_range < true > ( lookup_range , tex_height > 1 ? tex_pitch : 0 , lookup_mask ) ;
2019-02-25 16:03:14 +01:00
for ( auto & cached_texture : overlapping_locals )
{
2019-03-26 18:59:41 +01:00
if ( cached_texture - > matches ( texaddr , format , tex_width , tex_height , depth , 0 ) )
2019-02-25 16:03:14 +01:00
{
return { cached_texture - > get_view ( tex . remap ( ) , tex . decoded_remap ( ) ) , cached_texture - > get_context ( ) , cached_texture - > is_depth_texture ( ) , scale_x , scale_y , cached_texture - > get_image_type ( ) } ;
2017-10-23 17:17:54 +02:00
}
}
2019-02-25 16:03:14 +01:00
if ( ! is_compressed_format )
2017-09-14 13:37:14 +02:00
{
2019-02-25 16:03:14 +01:00
// Next, attempt to merge blit engine and surface store
// Blit sources contain info from any shader-read stuff in range
// NOTE: Compressed formats require a reupload, facilitated by blit synchronization and/or WCB and are not handled here
u32 required_surface_height , slice_h ;
switch ( extended_dimension )
{
case rsx : : texture_dimension_extended : : texture_dimension_3d :
case rsx : : texture_dimension_extended : : texture_dimension_cubemap :
// Account for padding between mipmaps for all layers
required_surface_height = tex_range . length ( ) / tex_pitch ;
slice_h = required_surface_height / depth ;
break ;
default :
// Ignore mipmaps and search for LOD0
required_surface_height = slice_h = tex_height ;
break ;
}
2017-09-14 13:37:14 +02:00
2019-03-20 23:55:30 +01:00
const auto bpp = get_format_block_size_in_bytes ( format ) ;
const auto overlapping_fbos = m_rtts . get_merged_texture_memory_region ( cmd , texaddr , tex_width , required_surface_height , tex_pitch , bpp ) ;
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 ;
if ( LIKELY ( overlapping_locals . empty ( ) ) )
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
const auto & last = overlapping_fbos . back ( ) ;
2019-03-29 20:04:54 +01:00
if ( last . src_x = = 0 & & last . src_y = = 0 )
2017-09-18 19:22:34 +02:00
{
2019-02-25 16:03:14 +01:00
u16 internal_width = tex_width ;
u16 internal_height = required_surface_height ;
get_native_dimensions ( internal_width , internal_height , last . surface ) ;
2017-09-18 19:22:34 +02:00
2019-03-29 20:04:54 +01:00
u16 normalized_width = u16 ( last . width * last . surface - > get_bpp ( ) ) / bpp ;
if ( normalized_width > = internal_width & & last . height > = internal_height )
2017-09-18 19:22:34 +02:00
{
2019-03-11 21:18:22 +01:00
return process_framebuffer_resource_fast ( cmd , last . surface , texaddr , format , tex_width , tex_height , depth ,
scale_x , scale_y , extended_dimension , tex . remap ( ) , tex . decoded_remap ( ) , false ) ;
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-03-29 20:04:54 +01:00
const auto normalized_width = u16 ( last - > get_width ( ) * get_format_block_size_in_bytes ( last - > get_gcm_format ( ) ) ) / bpp ;
2019-02-28 11:42:51 +01:00
if ( last - > get_section_base ( ) = = texaddr & &
2019-03-29 20:04:54 +01:00
normalized_width > = tex_width & & last - > get_height ( ) > = tex_height )
2019-02-28 11:42:51 +01:00
{
2019-03-29 20:04:54 +01:00
bool is_depth = last - > is_depth_texture ( ) ;
u32 gcm_format = format ;
if ( const auto gcm_format_is_depth = is_gcm_depth_format ( format ) ;
is_depth ! = gcm_format_is_depth )
{
// Conflict, resolve
if ( gcm_format_is_depth )
{
is_depth = true ;
}
else
{
const auto actual_format = last - > get_gcm_format ( ) ;
bool resolved = false ;
switch ( format )
{
case CELL_GCM_TEXTURE_A8R8G8B8 :
case CELL_GCM_TEXTURE_D8R8G8B8 :
{
// Compatible with D24S8_UINT
if ( actual_format = = CELL_GCM_TEXTURE_DEPTH24_D8 )
{
gcm_format = CELL_GCM_TEXTURE_DEPTH24_D8 ;
resolved = true ;
is_depth = true ;
}
break ;
}
case CELL_GCM_TEXTURE_X16 :
{
// Compatible with DEPTH16_UNORM
if ( actual_format = = CELL_GCM_TEXTURE_DEPTH16 )
{
gcm_format = CELL_GCM_TEXTURE_DEPTH16 ;
resolved = true ;
is_depth = true ;
}
break ;
}
}
if ( ! resolved )
{
LOG_ERROR ( RSX , " Reading texture with gcm format 0x%x as unexpected cast with format 0x%x " ,
actual_format , format ) ;
is_depth = gcm_format_is_depth ;
}
}
}
return { last - > get_raw_texture ( ) , deferred_request_command : : copy_image_static , texaddr , gcm_format , 0 , 0 ,
tex_width , tex_height , 1 , last - > get_context ( ) , is_depth ,
2019-03-04 19:22:45 +01:00
scale_x , scale_y , extended_dimension , tex . decoded_remap ( ) } ;
2019-02-28 11:42:51 +01:00
}
}
2019-02-25 16:03:14 +01:00
2019-02-28 11:42:51 +01:00
auto result = merge_cache_resources ( cmd , overlapping_fbos , overlapping_locals ,
2019-03-11 21:18:22 +01:00
texaddr , format , tex_width , tex_height , depth , tex_pitch , slice_h ,
scale_x , scale_y , extended_dimension , tex . remap ( ) , tex . decoded_remap ( ) , _pool ) ;
2019-02-28 11:42:51 +01:00
2019-03-13 13:38:58 +01:00
if ( ! result . external_subresource_desc . sections_to_copy . empty ( ) & &
( _pool = = 0 | | result . atlas_covers_target_area ( ) ) )
2019-02-28 11:42:51 +01:00
{
2019-03-13 13:38:58 +01:00
// TODO: Overlapped section persistance is required for framebuffer resources to work with this!
// Yellow filter in SCV is because of a 384x384 surface being reused as 160x90 (and likely not getting written to)
// Its then sampled again here as 384x384 and this does not work! (obviously)
2019-04-01 17:45:19 +02:00
// Optionally disallow caching if resource is being written to as it is being read from
for ( const auto & section : overlapping_fbos )
{
if ( m_rtts . address_is_bound ( section . base_address ) )
{
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-02-28 11:42:51 +01:00
return result ;
}
2019-03-04 19:22:45 +01:00
else
{
2019-03-13 13:38:58 +01:00
LOG_ERROR ( RSX , " Area merge failed! addr=0x%x, w=%d, h=%d, gcm_format=0x%x[sz=%d] " , texaddr , tex_width , tex_height , format , ! ( tex . format ( ) & CELL_GCM_TEXTURE_LN ) ) ;
for ( const auto & s : overlapping_locals )
{
if ( s - > get_context ( ) = = rsx : : texture_upload_context : : blit_engine_dst )
{
LOG_ERROR ( RSX , " Btw, you're about to lose a blit surface at 0x%x " , s - > get_section_base ( ) ) ;
}
}
2019-03-04 19:22:45 +01:00
//LOG_TRACE(RSX, "Partial memory recovered from cache; may require WCB/WDB to properly gather all the data");
}
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-02-25 16:03:14 +01:00
// Do direct upload from CPU as the last resort
const bool is_swizzled = ! ( tex . format ( ) & CELL_GCM_TEXTURE_LN ) ;
auto subresources_layout = get_subresources_layout ( tex ) ;
2017-09-08 16:52:13 +02:00
2019-02-25 16:03:14 +01:00
bool is_depth_format = false ;
switch ( format )
{
case CELL_GCM_TEXTURE_DEPTH16 :
case CELL_GCM_TEXTURE_DEPTH16_FLOAT :
case CELL_GCM_TEXTURE_DEPTH24_D8 :
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT :
is_depth_format = true ;
break ;
}
2018-09-22 02:14:26 +02:00
2019-02-25 16:03:14 +01:00
lock . upgrade ( ) ;
2018-02-21 21:43:05 +01:00
2019-02-25 16:03:14 +01:00
//Invalidate
invalidate_range_impl_base ( cmd , tex_range , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-23 17:17:54 +02:00
2019-02-25 16:03:14 +01:00
//NOTE: SRGB correction is to be handled in the fragment shader; upload as linear RGB
return { upload_image_from_cpu ( cmd , tex_range , tex_width , tex_height , depth , tex . get_exact_mipmap_count ( ) , tex_pitch , format ,
texture_upload_context : : shader_read , subresources_layout , extended_dimension , is_swizzled ) - > get_view ( tex . remap ( ) , tex . decoded_remap ( ) ) ,
texture_upload_context : : shader_read , is_depth_format , scale_x , scale_y , 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 ;
const u32 dst_address = ( u32 ) ( ( u64 ) dst . pixels - ( u64 ) vm : : base ( 0 ) ) ;
2019-03-16 11:58:15 +01:00
u32 src_address = ( u32 ) ( ( u64 ) src . pixels - ( u64 ) vm : : base ( 0 ) ) ;
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-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...
2018-04-21 20:47:58 +02:00
u16 src_w = ( u16 ) ( ( f32 ) dst . clip_width / scale_x ) ;
u16 src_h = ( u16 ) ( ( f32 ) 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 ;
}
}
if ( UNLIKELY ( ( src_h + src . offset_y ) > src . height ) )
{
// TODO: Special case that needs wrapping around (custom blit)
LOG_ERROR ( RSX , " Transfer cropped in Y, src_h=%d, offset_y=%d, block_h=%d " , src_h , src . offset_y , src . height ) ;
src_h = src . height - src . offset_y ;
dst_h = u16 ( src_h * scale_y + 0.000001f ) ;
}
if ( UNLIKELY ( ( src_w + src . offset_x ) > src . width ) )
{
// TODO: Special case that needs wrapping around (custom blit)
LOG_ERROR ( RSX , " Transfer cropped in X, src_w=%d, offset_x=%d, block_w=%d " , src_w , src . offset_x , src . width ) ;
src_w = src . width - src . offset_x ;
dst_w = u16 ( src_w * scale_x + 0.000001f ) ;
}
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 ;
}
2019-03-25 14:21:19 +01:00
auto rtt_lookup = [ & m_rtts , & cmd , & scale_x , & scale_y , this ] ( u32 address , u32 width , u32 height , u32 pitch , u8 bpp , bool allow_clipped ) - > typename surface_store_type : : surface_overlap_info
2019-03-04 12:24:15 +01:00
{
2019-03-20 23:55:30 +01:00
const auto list = m_rtts . get_merged_texture_memory_region ( cmd , address , width , height , pitch , bpp ) ;
2019-03-25 14:21:19 +01:00
if ( list . empty ( ) )
2019-03-04 12:24:15 +01:00
{
return { } ;
}
2019-03-25 14:21:19 +01:00
if ( list . back ( ) . is_clipped & & ! allow_clipped )
{
for ( auto It = list . rbegin ( ) ; It ! = list . rend ( ) ; + + It )
{
if ( ! It - > is_clipped )
{
return * It ;
}
auto _w = u32 ( It - > width * It - > surface - > get_bpp ( ) ) / bpp ;
auto _h = u32 ( It - > height ) ;
get_rsx_dimensions ( _w , _h , It - > surface ) ;
if ( _w < width )
{
if ( ( _w * scale_x ) < = 1.f )
continue ;
}
if ( _h < height )
{
if ( ( _h * scale_y ) < = 1.f )
continue ;
}
// Some surface exists, but its size is questionable
// Opt to re-upload (needs WCB/WDB to work properly)
break ;
}
return { } ;
}
2019-03-04 12:24:15 +01:00
return list . back ( ) ;
} ;
// Check if src/dst are parts of render targets
2019-03-20 23:55:30 +01:00
auto dst_subres = rtt_lookup ( dst_address , dst_w , dst_h , dst . pitch , dst_bpp , false ) ;
2017-09-18 19:22:34 +02:00
dst_is_render_target = dst_subres . surface ! = nullptr ;
2019-03-07 16:17:00 +01: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
2019-03-25 14:21:19 +01:00
auto src_subres = rtt_lookup ( src_address , src_w , src_h , src . pitch , src_bpp , false ) ;
2017-09-18 19:22:34 +02:00
src_is_render_target = src_subres . surface ! = nullptr ;
2019-03-07 16:17:00 +01:00
// Always use GPU blit if src or dst is in the surface store
2017-09-18 19:22:34 +02:00
if ( ! g_cfg . video . use_gpu_texture_scaling & & ! ( src_is_render_target | | dst_is_render_target ) )
return false ;
2019-03-11 18:34:34 +01:00
// Check if trivial memcpy can perform the same task
// Used to copy programs and arbitrary data to the GPU in some cases
if ( ! src_is_render_target & & ! dst_is_render_target & & dst_is_argb8 = = src_is_argb8 & & ! dst . swizzled )
{
2019-03-25 14:21:19 +01:00
if ( ( src_h = = 1 & & dst_h = = 1 ) | | ( dst_w = = src_w & & dst_h = = src_h & & src . pitch = = dst . pitch ) )
2019-03-11 18:34:34 +01:00
{
if ( dst . scale_x > 0.f & & dst . scale_y > 0.f )
{
2019-03-16 11:58:15 +01:00
const u32 memcpy_bytes_length = dst . clip_width * dst_bpp * dst . clip_height ;
2019-03-11 18:34:34 +01:00
std : : lock_guard lock ( m_cache_mutex ) ;
invalidate_range_impl_base ( cmd , address_range : : start_length ( src_address , memcpy_bytes_length ) , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
invalidate_range_impl_base ( cmd , address_range : : start_length ( dst_address , memcpy_bytes_length ) , invalidation_cause : : write , std : : forward < Args > ( extras ) . . . ) ;
memcpy ( dst . pixels , src . pixels , memcpy_bytes_length ) ;
return true ;
}
else
{
// Rotation transform applied, use fallback
return false ;
}
}
}
2019-03-22 12:10:29 +01:00
// Sanity and format compatibility checks
if ( dst_is_render_target )
{
if ( src_subres . is_depth ! = dst_subres . is_depth )
{
// Create a cache-local resource to resolve later
// TODO: Support depth->RGBA typeless transfer for vulkan
dst_is_render_target = false ;
}
}
2018-04-07 17:16:52 +02:00
if ( src_is_render_target )
{
2019-01-05 11:12:36 +01:00
src_subres . surface - > read_barrier ( cmd ) ;
2018-04-07 17:16:52 +02:00
const auto surf = src_subres . surface ;
2019-03-20 23:55:30 +01:00
const auto bpp = surf - > get_bpp ( ) ;
2019-03-16 11:58:15 +01:00
if ( bpp ! = src_bpp )
2018-04-07 17:16:52 +02:00
{
//Enable type scaling in src
typeless_info . src_is_typeless = true ;
2019-03-04 12:24:15 +01:00
typeless_info . src_is_depth = src_subres . is_depth ;
2019-03-16 11:58:15 +01:00
typeless_info . src_scaling_hint = ( f32 ) bpp / src_bpp ;
2018-04-07 17:16:52 +02:00
typeless_info . src_gcm_format = src_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
}
}
if ( dst_is_render_target )
{
2019-01-05 11:12:36 +01:00
// Full barrier is required in case of partial transfers
dst_subres . surface - > read_barrier ( cmd ) ;
2019-03-20 23:55:30 +01:00
auto bpp = dst_subres . surface - > get_bpp ( ) ;
2019-03-16 11:58:15 +01:00
if ( bpp ! = dst_bpp )
2018-04-07 17:16:52 +02:00
{
//Enable type scaling in dst
typeless_info . dst_is_typeless = true ;
2019-03-04 12:24:15 +01:00
typeless_info . dst_is_depth = dst_subres . is_depth ;
2019-03-16 11:58:15 +01:00
typeless_info . dst_scaling_hint = ( f32 ) bpp / dst_bpp ;
2018-04-07 17:16:52 +02:00
typeless_info . dst_gcm_format = dst_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
}
}
2019-03-07 16:17:00 +01:00
section_storage_type * cached_dest = nullptr ;
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
2019-03-16 11:58:15 +01:00
size2i dst_dimensions = { dst . pitch / dst_bpp , dst . height } ;
2017-10-24 15:22:53 +02:00
if ( src_is_render_target )
{
if ( dst_dimensions . width = = src_subres . surface - > get_surface_width ( ) )
2019-03-11 18:34:34 +01:00
{
2017-10-24 15:22:53 +02:00
dst_dimensions . height = std : : max ( src_subres . surface - > get_surface_height ( ) , dst . height ) ;
2019-03-11 18:34:34 +01:00
}
2019-03-25 14:21:19 +01:00
else if ( LIKELY ( dst_dimensions . width = = 1280 | | dst_dimensions . width = = 2560 ) )
2019-03-11 18:34:34 +01:00
{
// Optimizations table based on common width/height pairings. If we guess wrong, the upload resolver will fix it anyway
// TODO: Add more entries based on empirical data
2019-03-25 14:21:19 +01:00
dst_dimensions . height = std : : max < s32 > ( dst . height , 720 ) ;
}
else
{
//LOG_TRACE(RSX, "Blit transfer to surface with dims %dx%d", dst_dimensions.width, dst.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
2019-02-25 16:03:14 +01:00
auto overlapping_surfaces = find_texture_from_range ( address_range : : start_length ( dst_address , dst . pitch * dst . clip_height ) , dst . pitch , rsx : : texture_upload_context : : blit_engine_dst ) ;
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 ;
}
const auto this_address = surface - > get_section_base ( ) ;
if ( this_address > dst_address )
{
continue ;
}
if ( const u32 address_offset = dst_address - this_address )
2017-09-08 16:52:13 +02:00
{
2017-09-19 14:46:16 +02:00
const u16 offset_y = address_offset / dst . pitch ;
const u16 offset_x = address_offset % dst . pitch ;
2019-03-16 11:58:15 +01:00
const u16 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
2017-09-19 14:46:16 +02:00
if ( ( unsigned ) dst_area . x2 < = surface - > get_width ( ) & &
( unsigned ) dst_area . y2 < = surface - > get_height ( ) )
{
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
}
}
else
{
2019-03-04 12:24:15 +01:00
// Destination dimensions are relaxed (true)
2019-03-20 23:55:30 +01:00
dst_area = dst_subres . get_src_area ( ) ;
2017-09-08 16:52:13 +02:00
dest_texture = dst_subres . surface - > get_surface ( ) ;
2018-07-26 19:52:22 +02:00
typeless_info . dst_context = texture_upload_context : : framebuffer_storage ;
2017-09-08 16:52:13 +02:00
2018-04-07 17:16:52 +02:00
max_dst_width = ( u16 ) ( dst_subres . surface - > get_surface_width ( ) * typeless_info . dst_scaling_hint ) ;
2017-09-08 16:52:13 +02:00
max_dst_height = dst_subres . surface - > get_surface_height ( ) ;
}
2019-03-22 12:10:29 +01:00
// Check if available target is acceptable
// TODO: Check for other types of format mismatch
bool format_mismatch = false ;
if ( cached_dest )
2019-03-04 19:22:45 +01:00
{
2019-03-22 12:10:29 +01:00
if ( cached_dest - > is_depth_texture ( ) ! = src_subres . is_depth )
2019-03-04 19:22:45 +01:00
{
2019-03-22 12:10:29 +01:00
// Dest surface has the wrong 'aspect'
format_mismatch = true ;
2019-03-04 19:22:45 +01:00
}
2019-03-22 12:10:29 +01:00
else
2019-03-04 19:22:45 +01:00
{
2019-03-22 12:10:29 +01:00
// Check if it matches the transfer declaration
switch ( cached_dest - > get_gcm_format ( ) )
{
case CELL_GCM_TEXTURE_A8R8G8B8 :
case CELL_GCM_TEXTURE_DEPTH24_D8 :
format_mismatch = ! dst_is_argb8 ;
break ;
case CELL_GCM_TEXTURE_R5G6B5 :
case CELL_GCM_TEXTURE_DEPTH16 :
format_mismatch = dst_is_argb8 ;
break ;
default :
format_mismatch = true ;
break ;
}
2019-03-04 19:22:45 +01:00
}
}
if ( format_mismatch )
{
2019-03-07 16:17:00 +01:00
// The invalidate call before creating a new target will remove this section
2019-03-04 19:22:45 +01:00
cached_dest = nullptr ;
dest_texture = 0 ;
2019-03-22 12:10:29 +01:00
dst_area = old_dst_area ;
2019-03-04 19:22:45 +01:00
}
2019-02-25 16:03:14 +01:00
// Create source texture if does not exist
2017-09-08 16:52:13 +02:00
if ( ! src_is_render_target )
{
2019-02-25 16:03:14 +01:00
const u32 lookup_mask = rsx : : texture_upload_context : : blit_engine_src | rsx : : texture_upload_context : : blit_engine_dst ;
2019-03-07 16:17:00 +01:00
auto overlapping_surfaces = find_texture_from_range < false > ( address_range : : start_length ( src_address , src . pitch * src . height ) , src . pitch , lookup_mask ) ;
2017-09-08 16:52:13 +02:00
2017-11-10 13:13:32 +01:00
auto old_src_area = src_area ;
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-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
{
const u16 offset_y = address_offset / src . pitch ;
const u16 offset_x = address_offset % src . pitch ;
2019-03-16 11:58:15 +01:00
const u16 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 ( ) )
{
vram_texture = surface - > get_raw_texture ( ) ;
2018-07-26 19:52:22 +02:00
typeless_info . src_context = surface - > get_context ( ) ;
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
if ( ! vram_texture )
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 ;
if ( dst . scale_x > 0.f & & dst . scale_y > 0.f )
{
// 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 if ( ! src . offset_x & & ! src . offset_y )
{
if ( dst . scale_y < 0.f )
{
image_base = src . rsx_address - ( src . pitch * src . height ) ;
}
else
{
// Reverse X without reverse Y and no offset in X. Is this even possible?
LOG_ERROR ( RSX , " Unexpected scaling parameters: reversed X without reverse Y " ) ;
image_base = src . rsx_address - src . pitch ;
}
}
else
{
// It is difficult to determine the transfer region
image_base = src_address ;
image_width = src_w ;
image_height = src_h ;
}
2019-03-25 14:21:19 +01:00
2017-09-14 13:37:14 +02:00
lock . upgrade ( ) ;
2019-04-11 16:16:02 +02:00
const auto rsx_range = address_range : : start_length ( image_base , src . pitch * image_height ) ;
2018-12-29 14:28:12 +01:00
invalidate_range_impl_base ( cmd , rsx_range , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-09-08 16:52:13 +02:00
std : : vector < rsx_subresource_layout > subresource_layout ;
rsx_subresource_layout subres = { } ;
2019-04-11 16:16:02 +02:00
subres . width_in_block = image_width ;
subres . height_in_block = image_height ;
subres . pitch_in_block = full_width ;
2017-09-08 16:52:13 +02:00
subres . depth = 1 ;
2019-04-11 16:16:02 +02:00
subres . data = { reinterpret_cast < const gsl : : byte * > ( vm : : base ( image_base ) ) , src . pitch * image_height } ;
2017-09-08 16:52:13 +02:00
subresource_layout . push_back ( subres ) ;
const u32 gcm_format = src_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
2019-04-11 16:16:02 +02:00
vram_texture = upload_image_from_cpu ( cmd , rsx_range , image_width , image_height , 1 , 1 , src . pitch , gcm_format , texture_upload_context : : blit_engine_src ,
2018-07-17 18:42:51 +02:00
subresource_layout , rsx : : texture_dimension_extended : : texture_dimension_2d , dst . swizzled ) - > get_raw_texture ( ) ;
2017-10-21 23:12:32 +02:00
2018-07-26 19:52:22 +02:00
typeless_info . src_context = texture_upload_context : : blit_engine_src ;
2017-09-08 16:52:13 +02:00
}
}
else
{
2019-03-20 23:55:30 +01:00
src_area = src_subres . get_src_area ( ) ;
2017-09-08 16:52:13 +02:00
vram_texture = src_subres . surface - > get_surface ( ) ;
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
}
2019-03-22 12:10:29 +01:00
// Type of blit decided by the source, destination use should adapt on the fly
const bool is_depth_blit = src_subres . is_depth ;
2017-12-07 13:08:11 +01:00
u32 gcm_format ;
2019-03-22 12:10:29 +01:00
2017-12-07 13:08:11 +01:00
if ( is_depth_blit )
gcm_format = ( dst_is_argb8 ) ? CELL_GCM_TEXTURE_DEPTH24_D8 : CELL_GCM_TEXTURE_DEPTH16 ;
else
gcm_format = ( dst_is_argb8 ) ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
if ( cached_dest )
{
2019-03-04 19:22:45 +01:00
// Prep surface
2017-12-07 18:46:49 +01:00
auto channel_order = src_is_render_target ? rsx : : texture_create_flags : : native_component_order :
dst_is_argb8 ? rsx : : texture_create_flags : : default_component_order :
rsx : : texture_create_flags : : swapped_native_component_order ;
2017-12-07 13:08:11 +01:00
enforce_surface_creation_type ( * cached_dest , gcm_format , channel_order ) ;
}
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 )
{
2018-04-07 17:16:52 +02:00
const u16 scaled_clip_offset_x = ( const u16 ) ( ( f32 ) dst . clip_x / ( scale_x * typeless_info . src_scaling_hint ) ) ;
2017-09-15 00:32:23 +02:00
const u16 scaled_clip_offset_y = ( const u16 ) ( ( f32 ) 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 ;
}
if ( dest_texture = = 0 )
{
2018-09-18 20:06:34 +02:00
verify ( HERE ) , ! dst_is_render_target ;
// Need to calculate the minium required size that will fit the data, anchored on the rsx_address
// If the application starts off with an 'inseted' section, the guessed dimensions may not fit!
2019-03-25 14:21:19 +01:00
const u32 write_end = dst_address + ( dst . pitch * dst_h ) ;
2018-09-18 20:06:34 +02:00
const u32 expected_end = dst . rsx_address + ( dst . pitch * dst_dimensions . height ) ;
const u32 section_length = std : : max ( write_end , expected_end ) - dst . rsx_address ;
dst_dimensions . height = section_length / dst . pitch ;
2017-09-14 13:37:14 +02:00
2018-09-18 20:06:34 +02:00
lock . upgrade ( ) ;
2018-12-13 11:23:58 +01:00
2019-03-07 16:17:00 +01:00
// NOTE: Invalidating for read also flushes framebuffers locked in the range and invalidates them (obj->test() will fail)
2018-12-13 11:23:58 +01:00
const auto rsx_range = address_range : : start_length ( dst . rsx_address , section_length ) ;
2019-03-07 16:17:00 +01:00
// NOTE: Write flag set to remove all other overlapping regions (e.g shader_read or blit_src)
2018-12-29 14:28:12 +01:00
invalidate_range_impl_base ( cmd , rsx_range , invalidation_cause : : write , std : : forward < Args > ( extras ) . . . ) ;
2018-09-18 20:06:34 +02:00
// render target data is already in correct swizzle layout
2017-12-19 13:37:24 +01:00
auto channel_order = src_is_render_target ? rsx : : texture_create_flags : : native_component_order :
dst_is_argb8 ? rsx : : texture_create_flags : : default_component_order :
rsx : : texture_create_flags : : swapped_native_component_order ;
2019-03-21 18:22:03 +01:00
// Translate dst_area into the 'full' dst block based on dst.rsx_address as (0, 0)
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
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 ,
gcm_format , rsx : : texture_upload_context : : blit_engine_dst , rsx : : texture_dimension_extended : : texture_dimension_2d ,
channel_order ) ;
}
else
{
2019-03-16 11:58:15 +01:00
const u16 pitch_in_block = dst . pitch / dst_bpp ;
2019-03-13 13:38:58 +01:00
std : : vector < rsx_subresource_layout > subresource_layout ;
rsx_subresource_layout subres = { } ;
subres . width_in_block = dst_dimensions . width ;
subres . height_in_block = dst_dimensions . height ;
subres . pitch_in_block = pitch_in_block ;
subres . depth = 1 ;
2019-03-15 13:22:22 +01:00
subres . data = { reinterpret_cast < const gsl : : byte * > ( vm : : base ( dst . rsx_address ) ) , dst . pitch * dst_dimensions . height } ;
2019-03-13 13:38:58 +01: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 ,
gcm_format , rsx : : texture_upload_context : : blit_engine_dst , subresource_layout ,
rsx : : texture_dimension_extended : : texture_dimension_2d , false ) ;
enforce_surface_creation_type ( * cached_dest , gcm_format , channel_order ) ;
}
2017-10-21 23:12:32 +02:00
2018-05-10 13:50:32 +02:00
dest_texture = cached_dest - > get_raw_texture ( ) ;
2018-07-26 19:52:22 +02:00
typeless_info . dst_context = texture_upload_context : : blit_engine_dst ;
2017-09-08 16:52:13 +02:00
}
2018-05-10 13:50:32 +02:00
2019-04-01 17:45:19 +02:00
// Calculate number of bytes actually modified
u32 mem_length ;
const u32 mem_base = dst_address - dst . rsx_address ;
if ( dst . clip_height = = 1 )
2018-02-10 17:21:16 +01:00
{
2019-04-01 17:45:19 +02:00
mem_length = dst . clip_width * dst_bpp ;
}
else
{
const u32 mem_excess = mem_base % dst . pitch ;
mem_length = ( dst . pitch * dst . clip_height ) - mem_excess ;
}
2018-05-10 13:50:32 +02:00
2019-04-01 17:45:19 +02:00
// Invalidate any cached subresources in modified range
notify_surface_changed ( utils : : address_range : : start_length ( dst_address , mem_length ) ) ;
2018-09-10 12:22:24 +02:00
2019-04-01 17:45:19 +02:00
if ( cached_dest )
{
lock . upgrade ( ) ;
2018-09-10 12:22:24 +02:00
verify ( HERE ) , ( mem_base + mem_length ) < = cached_dest - > get_section_size ( ) ;
2018-05-10 13:50:32 +02:00
cached_dest - > reprotect ( utils : : protection : : no , { mem_base , mem_length } ) ;
2018-09-19 00:21:57 +02:00
cached_dest - > touch ( m_cache_update_tag ) ;
update_cache_tag ( ) ;
2018-02-10 17:21:16 +01:00
}
2018-05-10 13:50:32 +02:00
else
{
verify ( HERE ) , dst_is_render_target ;
2019-03-14 13:27:50 +01:00
dst_subres . surface - > on_write ( ) ;
2018-05-10 13:50:32 +02:00
}
2017-09-08 16:52:13 +02:00
2018-04-07 17:16:52 +02:00
if ( rsx : : get_resolution_scale_percent ( ) ! = 100 )
{
const f32 resolution_scale = rsx : : get_resolution_scale ( ) ;
if ( src_is_render_target )
{
if ( src_subres . surface - > get_surface_width ( ) > g_cfg . video . min_scalable_dimension )
{
src_area . x1 = ( u16 ) ( src_area . x1 * resolution_scale ) ;
src_area . x2 = ( u16 ) ( src_area . x2 * resolution_scale ) ;
}
2017-09-26 15:24:43 +02:00
2018-04-07 17:16:52 +02:00
if ( src_subres . surface - > get_surface_height ( ) > g_cfg . video . min_scalable_dimension )
{
src_area . y1 = ( u16 ) ( src_area . y1 * resolution_scale ) ;
src_area . y2 = ( u16 ) ( src_area . y2 * resolution_scale ) ;
}
}
if ( dst_is_render_target )
{
if ( dst_subres . surface - > get_surface_width ( ) > g_cfg . video . min_scalable_dimension )
{
dst_area . x1 = ( u16 ) ( dst_area . x1 * resolution_scale ) ;
dst_area . x2 = ( u16 ) ( dst_area . x2 * resolution_scale ) ;
}
if ( dst_subres . surface - > get_surface_height ( ) > g_cfg . video . min_scalable_dimension )
{
dst_area . y1 = ( u16 ) ( dst_area . y1 * resolution_scale ) ;
dst_area . y2 = ( u16 ) ( dst_area . y2 * resolution_scale ) ;
}
}
}
2017-09-26 15:24:43 +02:00
2018-04-07 17:16:52 +02:00
typeless_info . analyse ( ) ;
2018-12-29 14:28:12 +01:00
blitter . scale_image ( cmd , vram_texture , dest_texture , src_area , dst_area , interpolate , is_depth_blit , typeless_info ) ;
2018-02-10 17:21:16 +01:00
blit_op_result result = true ;
result . is_depth = is_depth_blit ;
if ( cached_dest )
{
result . real_dst_address = cached_dest - > get_section_base ( ) ;
result . real_dst_size = cached_dest - > get_section_size ( ) ;
}
else
{
result . real_dst_address = dst . rsx_address ;
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 ( )
{
if ( m_flush_always_cache . size ( ) )
{
if ( m_cache_update_tag . load ( std : : memory_order_consume ) ! = m_flush_always_update_timestamp )
{
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 )
{
2018-09-22 02:14:26 +02:00
verify ( HERE ) , section . exists ( ) ;
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 ( ) ;
2018-02-03 09:37:42 +01:00
m_flush_always_update_timestamp = m_cache_update_tag . load ( std : : memory_order_consume ) ;
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 ) ;
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
}
2018-10-28 14:59:39 +01:00
virtual const 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
}
2018-10-28 14:59:39 +01:00
const 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 ( ) ;
return ( num_flushes = = 0u ) ? 0.f : ( f32 ) m_misses_this_frame . load ( ) / num_flushes ;
2017-10-26 15:20:09 +02:00
}
2017-09-08 16:52:13 +02:00
} ;
}