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
} ;
2018-02-21 11:46:23 +01:00
struct copy_region_descriptor
{
image_resource_type src ;
u16 src_x ;
u16 src_y ;
u16 dst_x ;
u16 dst_y ;
2018-03-30 12:28:46 +02:00
u16 dst_z ;
2018-02-21 11:46:23 +01:00
u16 w ;
u16 h ;
} ;
2018-03-30 12:28:46 +02:00
enum deferred_request_command : u32
{
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 ;
2018-03-30 12:28:46 +02:00
deferred_request_command op ;
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 ;
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
u32 encoded_component_map ( ) const override
{
if ( image_handle )
{
return image_handle - > encoded_component_map ( ) ;
}
return 0 ;
}
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 ;
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 ( )
{
2017-11-04 14:29:54 +01:00
m_cache_update_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
{
2018-08-25 00:47:36 +02:00
logs : : RSX . error ( 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
{
2018-08-25 00:47:36 +02:00
logs : : RSX . warning ( 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 ) ;
} ) ;
}
for ( auto & surface : data . sections_to_flush )
{
if ( surface - > get_memory_read_flags ( ) = = rsx : : memory_read_flags : : flush_always )
{
// This region is set to always read from itself (unavoidable hard sync)
const auto ROP_timestamp = rsx : : get_current_renderer ( ) - > ROP_sync_timestamp ;
if ( surface - > is_synchronized ( ) & & ROP_timestamp > surface - > get_sync_timestamp ( ) )
{
2018-12-29 14:28:12 +01:00
surface - > copy_texture ( cmd , true , std : : forward < Args > ( extras ) . . . ) ;
2018-09-19 00:21:57 +02:00
}
}
2018-12-29 14:28:12 +01:00
surface - > flush ( cmd , std : : forward < Args > ( extras ) . . . ) ;
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 ( ) ) ;
2018-12-10 00:22:02 +01:00
AUDIT ( data . cause . is_read ( ) & & ! excluded - > is_flushable ( ) | | data . cause = = invalidation_cause : : superseded_by_fbo | | ! 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
{
// 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
( overlaps_fault_range & & cause = = invalidation_cause : : superseded_by_fbo & & tex . get_context ( ) = = texture_upload_context : : framebuffer_storage & & tex . get_section_base ( ) ! = fault_range_in . start )
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)
AUDIT ( cause = = invalidation_cause : : superseded_by_fbo | | 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 :
2017-12-18 10:02:19 +01:00
inline bool is_hw_blit_engine_compatible ( u32 format ) const
2017-09-19 14:46:16 +02:00
{
switch ( format )
{
case CELL_GCM_TEXTURE_A8R8G8B8 :
case CELL_GCM_TEXTURE_R5G6B5 :
case CELL_GCM_TEXTURE_DEPTH16 :
case CELL_GCM_TEXTURE_DEPTH24_D8 :
return true ;
default :
return false ;
}
}
2017-09-14 13:37:14 +02:00
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
}
std : : vector < section_storage_type * > find_texture_from_range ( const address_range & test_range )
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
2018-09-22 02:14:26 +02:00
// TODO ruipin: Removed as a workaround for a bug, will need to be fixed by kd-11
//if (tex.get_section_base() > test_range.start)
// continue;
2017-09-08 16:52:13 +02:00
2018-09-22 02:14:26 +02:00
if ( ! tex . is_dirty ( ) )
results . push_back ( & tex ) ;
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
}
2017-09-22 15:12:10 +02:00
section_storage_type * find_texture_from_dimensions ( u32 rsx_address , 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
{
2018-09-22 02:14:26 +02:00
if ( tex . matches ( rsx_address , width , height , depth , mipmaps ) & & ! tex . is_dirty ( ) )
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 >
2018-12-29 14:28:12 +01:00
void lock_memory_region ( commandbuffer_type & cmd , image_storage_type * image , const address_range & rsx_range , u32 width , u32 height , u32 pitch , std : : tuple < FlushArgs . . . > & & flush_extras , 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
2018-12-09 17:49:41 +01:00
std : : apply ( & texture_cache : : invalidate_range_impl_base < FlushArgs . . . > , std : : tuple_cat (
2018-12-29 14:28:12 +01:00
std : : forward_as_tuple ( this , cmd , rsx_range , invalidation_cause : : superseded_by_fbo ) ,
2018-12-09 17:49:41 +01:00
std : : forward < std : : tuple < FlushArgs . . . > > ( flush_extras )
) ) ;
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
}
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 )
{
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 | |
2018-03-30 12:28:46 +02:00
found_desc . op ! = desc . op | |
2017-11-03 12:16:55 +01:00
found_desc . x ! = desc . x | | found_desc . y ! = desc . y | |
found_desc . width ! = desc . width | | found_desc . height ! = desc . height )
continue ;
2018-03-30 12:28:46 +02:00
if ( desc . op = = deferred_request_command : : copy_image_dynamic )
2018-02-21 11:46:23 +01:00
update_image_contents ( cmd , It - > second . second , desc . external_handle , desc . width , desc . height ) ;
2017-11-03 12:16:55 +01:00
return It - > second . second ;
}
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 )
{
sections [ n ] = { desc . external_handle , 0 , ( u16 ) ( desc . height * n ) , 0 , 0 , n , desc . width , desc . height } ;
}
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 )
{
sections [ n ] = { desc . external_handle , 0 , ( u16 ) ( desc . height * n ) , 0 , 0 , n , desc . width , desc . height } ;
}
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 ;
}
void notify_surface_changed ( u32 base_address )
{
m_temporary_subresource_cache . erase ( base_address ) ;
}
2018-03-30 12:28:46 +02:00
template < typename surface_store_type >
2018-12-16 12:57:22 +01:00
std : : vector < copy_region_descriptor > gather_texture_slices_from_framebuffers ( commandbuffer_type & cmd ,
u32 texaddr , u16 slice_w , u16 slice_h , u16 pitch , u16 count , u8 bpp , surface_store_type & m_rtts )
2018-03-30 12:28:46 +02:00
{
std : : vector < copy_region_descriptor > surfaces ;
u32 current_address = texaddr ;
u32 slice_size = ( pitch * slice_h ) ;
bool unsafe = false ;
for ( u16 slice = 0 ; slice < count ; + + slice )
{
auto overlapping = m_rtts . get_merged_texture_memory_region ( current_address , slice_w , slice_h , pitch , bpp ) ;
current_address + = ( pitch * slice_h ) ;
if ( overlapping . empty ( ) )
{
unsafe = true ;
surfaces . push_back ( { } ) ;
}
else
{
for ( auto & section : overlapping )
{
2018-12-29 14:28:12 +01:00
section . surface - > read_barrier ( cmd ) ;
2018-12-16 12:57:22 +01:00
2018-03-30 12:28:46 +02:00
surfaces . push_back
( {
section . surface - > get_surface ( ) ,
rsx : : apply_resolution_scale ( section . src_x , true ) ,
rsx : : apply_resolution_scale ( section . src_y , true ) ,
rsx : : apply_resolution_scale ( section . dst_x , true ) ,
rsx : : apply_resolution_scale ( section . dst_y , true ) ,
slice ,
rsx : : apply_resolution_scale ( section . width , true ) ,
rsx : : apply_resolution_scale ( section . height , true )
} ) ;
}
}
}
if ( unsafe )
{
//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 " ) ;
}
return surfaces ;
}
2017-11-02 16:54:57 +01:00
template < typename render_target_type , typename surface_store_type >
2018-02-01 08:44:57 +01:00
sampled_image_descriptor process_framebuffer_resource ( commandbuffer_type & cmd , render_target_type texptr , u32 texaddr , u32 gcm_format , surface_store_type & m_rtts ,
2018-12-20 20:38:48 +01:00
u16 tex_width , u16 tex_height , u16 tex_depth , u16 tex_pitch , rsx : : texture_dimension_extended extended_dimension , bool is_depth , bool is_bound , u32 encoded_remap , const texture_channel_remap_t & decoded_remap )
2017-11-02 16:54:57 +01:00
{
2017-11-02 19:54:19 +01:00
const u32 format = gcm_format & ~ ( CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN ) ;
const auto surface_width = texptr - > get_surface_width ( ) ;
const auto surface_height = texptr - > get_surface_height ( ) ;
2018-03-30 12:28:46 +02:00
u32 internal_width = tex_width ;
u32 internal_height = tex_height ;
get_native_dimensions ( internal_width , internal_height , texptr ) ;
2018-12-29 14:28:12 +01:00
texptr - > read_barrier ( cmd ) ;
2018-12-16 12:57:22 +01:00
2017-11-02 16:54:57 +01:00
if ( extended_dimension ! = rsx : : texture_dimension_extended : : texture_dimension_2d & &
extended_dimension ! = rsx : : texture_dimension_extended : : texture_dimension_1d )
{
2017-11-02 19:54:19 +01:00
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_cubemap )
{
2018-03-30 12:28:46 +02:00
const auto scaled_size = rsx : : apply_resolution_scale ( internal_width , true ) ;
2017-11-02 19:54:19 +01:00
if ( surface_height = = ( surface_width * 6 ) )
{
2018-03-30 12:28:46 +02:00
return { texptr - > get_surface ( ) , deferred_request_command : : cubemap_unwrap , texaddr , format , 0 , 0 ,
scaled_size , scaled_size , 1 ,
texture_upload_context : : framebuffer_storage , is_depth , 1.f , 1.f ,
rsx : : texture_dimension_extended : : texture_dimension_cubemap , decoded_remap } ;
2017-11-02 19:54:19 +01:00
}
2018-03-30 12:28:46 +02:00
sampled_image_descriptor desc = { texptr - > get_surface ( ) , deferred_request_command : : cubemap_gather , texaddr , format , 0 , 0 ,
scaled_size , scaled_size , 1 ,
texture_upload_context : : framebuffer_storage , is_depth , 1.f , 1.f ,
rsx : : texture_dimension_extended : : texture_dimension_cubemap , decoded_remap } ;
2017-11-02 19:54:19 +01:00
2018-03-30 12:28:46 +02:00
auto bpp = get_format_block_size_in_bytes ( format ) ;
2018-12-16 12:57:22 +01:00
desc . external_subresource_desc . sections_to_copy = std : : move ( gather_texture_slices_from_framebuffers ( cmd , texaddr , tex_width , tex_height , tex_pitch , 6 , bpp , m_rtts ) ) ;
2018-03-30 12:28:46 +02:00
return desc ;
}
else if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_3d & & tex_depth > 1 )
{
auto minimum_height = ( tex_height * tex_depth ) ;
auto scaled_w = rsx : : apply_resolution_scale ( internal_width , true ) ;
auto scaled_h = rsx : : apply_resolution_scale ( internal_height , true ) ;
if ( surface_height > = minimum_height & & surface_width > = tex_width )
2017-11-02 19:54:19 +01:00
{
2018-03-30 12:28:46 +02: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 } ;
2017-11-02 19:54:19 +01:00
}
2017-11-03 13:48:27 +01:00
2018-03-30 12:28:46 +02:00
sampled_image_descriptor desc = { texptr - > get_surface ( ) , 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 } ;
2017-11-03 13:48:27 +01:00
2018-03-30 12:28:46 +02:00
const auto bpp = get_format_block_size_in_bytes ( format ) ;
2018-12-16 12:57:22 +01:00
desc . external_subresource_desc . sections_to_copy = std : : move ( gather_texture_slices_from_framebuffers ( cmd , texaddr , tex_width , tex_height , tex_pitch , tex_depth , bpp , m_rtts ) ) ;
2017-11-03 13:48:27 +01:00
return desc ;
2017-11-02 19:54:19 +01:00
}
2017-11-02 16:54:57 +01:00
}
2017-11-02 17:12:08 +01:00
const bool unnormalized = ( gcm_format & CELL_GCM_TEXTURE_UN ) ! = 0 ;
2017-11-02 19:54:19 +01:00
f32 scale_x = ( unnormalized ) ? ( 1.f / tex_width ) : 1.f ;
f32 scale_y = ( unnormalized ) ? ( 1.f / tex_height ) : 1.f ;
2017-11-02 16:54:57 +01:00
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d )
{
internal_height = 1 ;
scale_y = 0.f ;
}
2018-08-01 22:55:14 +02:00
auto bpp = get_format_block_size_in_bytes ( format ) ;
auto overlapping = m_rtts . get_merged_texture_memory_region ( texaddr , tex_width , tex_height , tex_pitch , bpp ) ;
2018-08-14 21:58:50 +02:00
bool requires_merging = false ;
2018-02-21 11:46:23 +01:00
2018-12-16 12:57:22 +01:00
AUDIT ( ! overlapping . empty ( ) ) ;
2018-08-01 22:55:14 +02:00
if ( overlapping . size ( ) > 1 )
2018-08-14 21:58:50 +02:00
{
// The returned values are sorted with oldest first and newest last
// This allows newer data to overwrite older memory when merging the list
if ( overlapping . back ( ) . surface = = texptr )
{
// The texture 'proposed' by the previous lookup is the newest one
// If it occupies the entire requested region, just use it as-is
requires_merging = ( internal_width > surface_width | | internal_height > surface_height ) ;
}
else
{
requires_merging = true ;
}
}
if ( requires_merging )
2018-08-01 22:55:14 +02:00
{
const auto w = rsx : : apply_resolution_scale ( internal_width , true ) ;
const auto h = rsx : : apply_resolution_scale ( internal_height , true ) ;
2018-02-21 11:46:23 +01:00
2018-08-01 22:55:14 +02:00
sampled_image_descriptor result = { texptr - > get_surface ( ) , 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 } ;
2018-02-21 11:46:23 +01:00
2018-08-01 22:55:14 +02:00
result . external_subresource_desc . sections_to_copy . reserve ( overlapping . size ( ) ) ;
2018-02-21 11:46:23 +01:00
2018-08-01 22:55:14 +02:00
for ( auto & section : overlapping )
{
2018-12-29 14:28:12 +01:00
section . surface - > read_barrier ( cmd ) ;
2018-12-16 12:57:22 +01:00
2018-08-01 22:55:14 +02:00
result . external_subresource_desc . sections_to_copy . push_back
( {
section . surface - > get_surface ( ) ,
rsx : : apply_resolution_scale ( section . src_x , true ) ,
rsx : : apply_resolution_scale ( section . src_y , true ) ,
rsx : : apply_resolution_scale ( section . dst_x , true ) ,
rsx : : apply_resolution_scale ( section . dst_y , true ) ,
0 ,
rsx : : apply_resolution_scale ( section . width , true ) ,
rsx : : apply_resolution_scale ( section . height , true )
} ) ;
2018-02-21 11:46:23 +01:00
}
2018-08-01 22:55:14 +02:00
return result ;
2018-02-21 11:46:23 +01:00
}
2018-02-20 14:29:03 +01:00
bool requires_processing = surface_width > internal_width | | surface_height > internal_height ;
2018-02-21 11:46:23 +01:00
bool update_subresource_cache = false ;
2017-11-02 16:54:57 +01:00
if ( ! requires_processing )
{
2018-02-20 14:29:03 +01:00
//NOTE: The scale also accounts for sampling outside the RTT region, e.g render to one quadrant but send whole texture for sampling
//In these cases, internal dimensions will exceed available surface dimensions. Account for the missing information using scaling (missing data will result in border color)
//TODO: Proper gather and stitching without performance loss
if ( internal_width > surface_width )
scale_x * = ( ( f32 ) internal_width / surface_width ) ;
if ( internal_height > surface_height )
scale_y * = ( ( f32 ) internal_height / surface_height ) ;
2018-12-20 20:38:48 +01:00
if ( is_bound )
2017-11-02 16:54:57 +01:00
{
2018-12-20 20:38:48 +01:00
if ( g_cfg . video . strict_rendering_mode )
2017-11-02 16:54:57 +01:00
{
2018-12-20 20:38:48 +01:00
LOG_TRACE ( RSX , " Attempting to sample a currently bound %s target @ 0x%x " , is_depth ? " depth " : " color " , texaddr ) ;
requires_processing = true ;
update_subresource_cache = true ;
2017-11-02 16:54:57 +01:00
}
2018-12-20 20:38:48 +01:00
else
2017-11-02 16:54:57 +01:00
{
2018-12-20 20:38:48 +01:00
// Issue a texture barrier to ensure previous writes are visible
insert_texture_barrier ( cmd , texptr ) ;
2017-11-02 16:54:57 +01:00
}
}
}
2018-02-23 20:49:59 +01:00
if ( ! requires_processing )
{
//Check if we need to do anything about the formats
requires_processing = ! render_target_format_is_compatible ( texptr , format ) ;
}
2017-11-02 16:54:57 +01:00
if ( requires_processing )
{
2018-06-02 21:48:08 +02:00
const auto w = rsx : : apply_resolution_scale ( std : : min < u16 > ( internal_width , surface_width ) , true ) ;
const auto h = rsx : : apply_resolution_scale ( std : : min < u16 > ( internal_height , surface_height ) , true ) ;
2018-02-21 11:46:23 +01:00
2018-03-30 12:28:46 +02:00
auto command = update_subresource_cache ? deferred_request_command : : copy_image_dynamic : deferred_request_command : : copy_image_static ;
return { texptr - > get_surface ( ) , command , 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 } ;
2017-11-02 16:54:57 +01:00
}
2018-03-30 12:28:46 +02: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 } ;
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 ) ;
2017-10-23 17:17:54 +02:00
const bool is_compressed_format = ( format = = CELL_GCM_TEXTURE_COMPRESSED_DXT1 | | format = = CELL_GCM_TEXTURE_COMPRESSED_DXT23 | | format = = CELL_GCM_TEXTURE_COMPRESSED_DXT45 ) ;
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 ( ) ;
2017-10-31 16:45:54 +01:00
const u16 tex_width = tex . width ( ) ;
2018-12-13 11:23:58 +01:00
u16 tex_height = tex . height ( ) ;
u16 tex_pitch = ( u16 ) tex . pitch ( ) ;
if ( tex_pitch = = 0 ) 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 ;
}
2017-10-23 17:17:54 +02:00
if ( ! is_compressed_format )
2017-09-08 16:52:13 +02:00
{
2018-12-20 20:38:48 +01:00
// Check for sampleable rtts from previous render passes
// TODO: When framebuffer Y compression is properly handled, this section can be removed. A more accurate framebuffer storage check exists below this block
2017-10-23 17:17:54 +02:00
if ( auto texptr = m_rtts . get_texture_from_render_target_if_applicable ( texaddr ) )
{
2019-01-05 11:12:36 +01:00
if ( const bool is_active = m_rtts . address_is_bound ( texaddr , false ) ;
is_active | | texptr - > test ( ) )
2017-10-26 15:20:09 +02:00
{
2018-03-18 12:40:26 +01:00
return process_framebuffer_resource ( cmd , texptr , texaddr , tex . format ( ) , m_rtts ,
2018-12-20 20:38:48 +01:00
tex_width , tex_height , depth , tex_pitch , extended_dimension , false , is_active ,
tex . remap ( ) , tex . decoded_remap ( ) ) ;
2017-10-26 15:20:09 +02:00
}
2017-10-30 13:27:22 +01:00
else
2017-10-31 16:45:54 +01:00
{
2017-10-30 13:27:22 +01:00
m_rtts . invalidate_surface_address ( texaddr , false ) ;
2018-12-29 14:28:12 +01:00
invalidate_address ( cmd , texaddr , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-31 16:45:54 +01:00
}
2017-10-26 15:20:09 +02:00
}
if ( auto texptr = m_rtts . get_texture_from_depth_stencil_if_applicable ( texaddr ) )
{
2019-01-23 22:40:53 +01:00
if ( const bool is_active = m_rtts . address_is_bound ( texaddr , true ) ;
2019-01-05 11:12:36 +01:00
is_active | | texptr - > test ( ) )
2017-10-23 17:17:54 +02:00
{
2018-03-18 12:40:26 +01:00
return process_framebuffer_resource ( cmd , texptr , texaddr , tex . format ( ) , m_rtts ,
2018-12-20 20:38:48 +01:00
tex_width , tex_height , depth , tex_pitch , extended_dimension , true , is_active ,
tex . remap ( ) , tex . decoded_remap ( ) ) ;
2017-09-08 16:52:13 +02:00
}
2017-10-30 13:27:22 +01:00
else
2017-10-31 16:45:54 +01:00
{
2017-10-30 13:27:22 +01:00
m_rtts . invalidate_surface_address ( texaddr , true ) ;
2018-12-29 14:28:12 +01:00
invalidate_address ( cmd , texaddr , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-31 16:45:54 +01:00
}
2017-09-08 16:52:13 +02:00
}
}
2017-11-03 16:25:59 +01:00
const bool unnormalized = ( tex . format ( ) & CELL_GCM_TEXTURE_UN ) ! = 0 ;
f32 scale_x = ( unnormalized ) ? ( 1.f / tex_width ) : 1.f ;
f32 scale_y = ( unnormalized ) ? ( 1.f / tex_height ) : 1.f ;
if ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d )
scale_y = 0.f ;
2017-10-23 17:17:54 +02:00
if ( ! is_compressed_format )
{
2018-03-18 12:40:26 +01:00
// Check if we are re-sampling a subresource of an RTV/DSV texture, bound or otherwise
2017-10-23 17:17:54 +02:00
2017-10-31 23:36:39 +01:00
const auto rsc = m_rtts . get_surface_subresource_if_applicable ( texaddr , tex_width , tex_height , tex_pitch ) ;
2017-10-30 13:27:22 +01:00
if ( rsc . surface )
2017-10-23 17:17:54 +02:00
{
2018-12-20 20:38:48 +01:00
if ( ! rsc . surface - > test ( ) & & ! m_rtts . address_is_bound ( rsc . base_address , rsc . is_depth_surface ) )
2017-10-30 13:27:22 +01:00
{
2017-10-31 16:45:54 +01:00
m_rtts . invalidate_surface_address ( rsc . base_address , rsc . is_depth_surface ) ;
2018-12-29 14:28:12 +01:00
invalidate_address ( cmd , rsc . base_address , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-30 13:27:22 +01:00
}
2017-11-02 16:54:57 +01:00
else if ( extended_dimension ! = rsx : : texture_dimension_extended : : texture_dimension_2d & &
extended_dimension ! = rsx : : texture_dimension_extended : : texture_dimension_1d )
2017-10-23 17:17:54 +02:00
{
LOG_ERROR ( RSX , " Sampling of RTT region as non-2D texture! addr=0x%x, Type=%d, dims=%dx%d " ,
texaddr , ( u8 ) tex . get_extended_texture_dimension ( ) , tex . width ( ) , tex . height ( ) ) ;
}
else
{
2017-11-05 15:57:00 +01:00
u16 internal_width = tex_width ;
u16 internal_height = tex_height ;
2017-11-02 16:54:57 +01:00
2017-11-05 15:57:00 +01:00
get_native_dimensions ( internal_width , internal_height , rsc . surface ) ;
2018-03-18 12:40:26 +01:00
if ( ! rsc . x & & ! rsc . y & & rsc . w = = internal_width & & rsc . h = = internal_height )
2017-10-23 17:17:54 +02:00
{
2018-03-18 12:40:26 +01:00
//Full sized hit from the surface cache. This should have been already found before getting here
fmt : : throw_exception ( " Unreachable " HERE ) ;
2017-10-23 17:17:54 +02:00
}
2017-11-02 16:54:57 +01:00
2017-11-05 15:57:00 +01:00
internal_width = rsx : : apply_resolution_scale ( internal_width , true ) ;
internal_height = ( extended_dimension = = rsx : : texture_dimension_extended : : texture_dimension_1d ) ? 1 : rsx : : apply_resolution_scale ( internal_height , true ) ;
2018-03-30 12:28:46 +02:00
return { rsc . surface - > get_surface ( ) , deferred_request_command : : copy_image_static , rsc . base_address , format ,
rsx : : apply_resolution_scale ( rsc . x , false ) , rsx : : apply_resolution_scale ( rsc . y , false ) ,
internal_width , internal_height , 1 , texture_upload_context : : framebuffer_storage , rsc . is_depth_surface , scale_x , scale_y ,
2018-03-18 12:40:26 +01:00
rsx : : texture_dimension_extended : : texture_dimension_2d , tex . decoded_remap ( ) } ;
2017-10-23 17:17:54 +02:00
}
}
}
2017-09-14 13:37:14 +02:00
{
//Search in cache and upload/bind
reader_lock lock ( m_cache_mutex ) ;
2017-09-22 15:12:10 +02:00
auto cached_texture = find_texture_from_dimensions ( texaddr , tex_width , tex_height , depth ) ;
2017-09-14 13:37:14 +02:00
if ( cached_texture )
{
2017-11-24 21:57:14 +01:00
//TODO: Handle invalidated framebuffer textures better. This is awful
if ( cached_texture - > get_context ( ) = = rsx : : texture_upload_context : : framebuffer_storage )
{
if ( ! cached_texture - > is_locked ( ) )
{
2018-09-22 02:14:26 +02:00
lock . upgrade ( ) ;
2017-11-24 21:57:14 +01:00
cached_texture - > set_dirty ( true ) ;
}
}
else
{
if ( cached_texture - > get_image_type ( ) = = rsx : : texture_dimension_extended : : texture_dimension_1d )
scale_y = 0.f ;
2017-11-02 16:54:57 +01:00
2018-07-17 18:42:51 +02: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-11-24 21:57:14 +01:00
}
2017-09-14 13:37:14 +02:00
}
2017-09-18 19:22:34 +02:00
2018-02-23 20:49:59 +01:00
if ( is_hw_blit_engine_compatible ( format ) )
2017-09-18 19:22:34 +02:00
{
2017-09-19 14:46:16 +02:00
//Find based on range instead
2018-09-22 02:14:26 +02:00
auto overlapping_surfaces = find_texture_from_range ( tex_range ) ;
2017-09-19 14:46:16 +02:00
if ( ! overlapping_surfaces . empty ( ) )
2017-09-18 19:22:34 +02:00
{
2018-02-10 09:52:44 +01:00
for ( const auto & surface : overlapping_surfaces )
2017-09-18 19:22:34 +02:00
{
2018-04-07 17:16:52 +02:00
if ( surface - > get_context ( ) ! = rsx : : texture_upload_context : : blit_engine_dst | |
2018-09-22 02:14:26 +02:00
! surface - > overlaps ( tex_range , rsx : : section_bounds : : confirmed_range ) )
2017-09-19 14:46:16 +02:00
continue ;
2017-09-18 19:22:34 +02:00
2017-09-19 14:46:16 +02:00
if ( surface - > get_width ( ) > = tex_width & & surface - > get_height ( ) > = tex_height )
2017-09-18 19:22:34 +02:00
{
2017-09-19 14:46:16 +02:00
u16 offset_x = 0 , offset_y = 0 ;
if ( const u32 address_offset = texaddr - surface - > get_section_base ( ) )
{
const auto bpp = get_format_block_size_in_bytes ( format ) ;
offset_y = address_offset / tex_pitch ;
offset_x = ( address_offset % tex_pitch ) / bpp ;
}
if ( ( offset_x + tex_width ) < = surface - > get_width ( ) & &
( offset_y + tex_height ) < = surface - > get_height ( ) )
{
2017-11-02 16:54:57 +01:00
if ( extended_dimension ! = rsx : : texture_dimension_extended : : texture_dimension_2d & &
extended_dimension ! = rsx : : texture_dimension_extended : : texture_dimension_1d )
2017-09-22 15:12:10 +02:00
{
LOG_ERROR ( RSX , " Texture resides in blit engine memory, but requested type is not 2D (%d) " , ( u32 ) extended_dimension ) ;
break ;
}
2017-09-19 14:46:16 +02:00
auto src_image = surface - > get_raw_texture ( ) ;
2018-03-30 12:28:46 +02:00
return { src_image , deferred_request_command : : copy_image_static , surface - > get_section_base ( ) , format , offset_x , offset_y , tex_width , tex_height , 1 ,
texture_upload_context : : blit_engine_dst , surface - > is_depth_texture ( ) , scale_x , scale_y , rsx : : texture_dimension_extended : : texture_dimension_2d ,
rsx : : default_remap_vector } ;
2017-09-19 14:46:16 +02:00
}
2017-09-18 19:22:34 +02:00
}
}
}
}
2017-09-14 13:37:14 +02:00
2018-09-22 02:14:26 +02: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
2018-09-22 02:14:26 +02: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 ;
}
// Upgrade lock
lock . upgrade ( ) ;
2018-02-21 21:43:05 +01:00
2018-09-22 02:14:26 +02:00
//Invalidate
2018-12-29 14:28:12 +01:00
invalidate_range_impl_base ( cmd , tex_range , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-23 17:17:54 +02:00
2018-09-22 02:14:26 +02:00
//NOTE: SRGB correction is to be handled in the fragment shader; upload as linear RGB
2018-12-13 11:23:58 +01:00
return { upload_image_from_cpu ( cmd , tex_range , tex_width , tex_height , depth , tex . get_exact_mipmap_count ( ) , tex_pitch , format ,
2018-09-22 02:14:26 +02:00
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
{
//Since we will have dst in vram, we can 'safely' ignore the swizzle flag
//TODO: Verify correct behavior
bool src_is_render_target = false ;
bool dst_is_render_target = false ;
bool dst_is_argb8 = ( dst . format = = rsx : : blit_engine : : transfer_destination_format : : a8r8g8b8 ) ;
bool src_is_argb8 = ( src . format = = rsx : : blit_engine : : transfer_source_format : : a8r8g8b8 ) ;
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 src_address = ( u32 ) ( ( u64 ) src . pixels - ( u64 ) vm : : base ( 0 ) ) ;
const u32 dst_address = ( u32 ) ( ( u64 ) dst . pixels - ( u64 ) vm : : base ( 0 ) ) ;
2018-04-07 17:16:52 +02:00
f32 scale_x = dst . scale_x ;
f32 scale_y = dst . scale_y ;
2017-09-08 16:52:13 +02:00
2017-09-18 19:22:34 +02: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
//Check if src/dst are parts of render targets
2017-10-31 23:36:39 +01:00
auto dst_subres = m_rtts . get_surface_subresource_if_applicable ( dst_address , dst . width , dst . clip_height , dst . pitch , true , false , false ) ;
2017-09-18 19:22:34 +02:00
dst_is_render_target = dst_subres . surface ! = nullptr ;
2017-10-12 14:48:31 +02:00
if ( dst_is_render_target & & dst_subres . surface - > get_native_pitch ( ) ! = dst . pitch )
{
//Surface pitch is invalid if it is less that the rsx pitch (usually set to 64 in such a case)
if ( dst_subres . surface - > get_rsx_pitch ( ) ! = dst . pitch | |
dst . pitch < dst_subres . surface - > get_native_pitch ( ) )
dst_is_render_target = false ;
}
2017-09-18 19:22:34 +02:00
//TODO: Handle cases where src or dst can be a depth texture while the other is a color texture - requires a render pass to emulate
2018-01-18 14:59:55 +01:00
auto src_subres = m_rtts . get_surface_subresource_if_applicable ( src_address , src_w , src_h , src . pitch , true , false , false ) ;
2017-09-18 19:22:34 +02:00
src_is_render_target = src_subres . surface ! = nullptr ;
2017-10-12 14:48:31 +02:00
if ( src_is_render_target & & src_subres . surface - > get_native_pitch ( ) ! = src . pitch )
{
//Surface pitch is invalid if it is less that the rsx pitch (usually set to 64 in such a case)
if ( src_subres . surface - > get_rsx_pitch ( ) ! = src . pitch | |
src . pitch < src_subres . surface - > get_native_pitch ( ) )
src_is_render_target = false ;
}
2018-12-20 20:38:48 +01:00
if ( src_is_render_target & & ! src_subres . surface - > test ( ) & & ! m_rtts . address_is_bound ( src_subres . base_address , src_subres . is_depth_surface ) )
2017-10-31 16:45:54 +01:00
{
m_rtts . invalidate_surface_address ( src_subres . base_address , src_subres . is_depth_surface ) ;
2018-12-29 14:28:12 +01:00
invalidate_address ( cmd , src_subres . base_address , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-31 16:45:54 +01:00
src_is_render_target = false ;
}
2018-12-20 20:38:48 +01:00
if ( dst_is_render_target & & ! dst_subres . surface - > test ( ) & & ! m_rtts . address_is_bound ( dst_subres . base_address , dst_subres . is_depth_surface ) )
2017-10-31 16:45:54 +01:00
{
m_rtts . invalidate_surface_address ( dst_subres . base_address , dst_subres . is_depth_surface ) ;
2018-12-29 14:28:12 +01:00
invalidate_address ( cmd , dst_subres . base_address , invalidation_cause : : read , std : : forward < Args > ( extras ) . . . ) ;
2017-10-31 16:45:54 +01:00
dst_is_render_target = false ;
}
2017-09-18 19:22:34 +02:00
//Always use GPU blit if src or dst is in the surface store
if ( ! g_cfg . video . use_gpu_texture_scaling & & ! ( src_is_render_target | | dst_is_render_target ) )
return 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 ;
auto src_bpp = surf - > get_native_pitch ( ) / surf - > get_surface_width ( ) ;
auto expected_bpp = src_is_argb8 ? 4 : 2 ;
if ( src_bpp ! = expected_bpp )
{
//Enable type scaling in src
typeless_info . src_is_typeless = true ;
typeless_info . src_is_depth = src_subres . is_depth_surface ;
typeless_info . src_scaling_hint = ( f32 ) src_bpp / expected_bpp ;
typeless_info . src_gcm_format = src_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
src_w = ( u16 ) ( src_w / typeless_info . src_scaling_hint ) ;
if ( ! src_subres . is_clipped )
src_subres . w = ( u16 ) ( src_subres . w / typeless_info . src_scaling_hint ) ;
else
src_subres = m_rtts . get_surface_subresource_if_applicable ( src_address , src_w , src_h , src . pitch , true , false , false ) ;
verify ( HERE ) , src_subres . surface ! = nullptr ;
}
}
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 ) ;
2018-04-07 17:16:52 +02:00
auto dst_bpp = dst_subres . surface - > get_native_pitch ( ) / dst_subres . surface - > get_surface_width ( ) ;
auto expected_bpp = dst_is_argb8 ? 4 : 2 ;
if ( dst_bpp ! = expected_bpp )
{
//Enable type scaling in dst
typeless_info . dst_is_typeless = true ;
typeless_info . dst_is_depth = dst_subres . is_depth_surface ;
typeless_info . dst_scaling_hint = ( f32 ) dst_bpp / expected_bpp ;
typeless_info . dst_gcm_format = dst_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
dst_w = ( u16 ) ( dst_w / typeless_info . dst_scaling_hint ) ;
if ( ! dst_subres . is_clipped )
dst_subres . w = ( u16 ) ( dst_subres . w / typeless_info . dst_scaling_hint ) ;
else
dst_subres = m_rtts . get_surface_subresource_if_applicable ( dst_address , dst_w , dst_h , dst . pitch , true , false , false ) ;
verify ( HERE ) , dst_subres . surface ! = nullptr ;
}
}
2017-10-12 14:48:31 +02:00
reader_lock lock ( m_cache_mutex ) ;
2017-09-08 16:52:13 +02:00
//Check if trivial memcpy can perform the same task
//Used to copy programs to the GPU in some cases
2017-09-19 14:46:16 +02:00
if ( ! src_is_render_target & & ! dst_is_render_target & & dst_is_argb8 = = src_is_argb8 & & ! dst . swizzled )
2017-09-08 16:52:13 +02:00
{
if ( ( src . slice_h = = 1 & & dst . clip_height = = 1 ) | |
( dst . clip_width = = src . width & & dst . clip_height = = src . slice_h & & src . pitch = = dst . pitch ) )
{
const u8 bpp = dst_is_argb8 ? 4 : 2 ;
2017-10-12 14:48:31 +02:00
const u32 memcpy_bytes_length = dst . clip_width * bpp * dst . clip_height ;
lock . upgrade ( ) ;
2018-12-29 14:28:12 +01:00
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 ) . . . ) ;
2017-10-12 14:48:31 +02:00
memcpy ( dst . pixels , src . pixels , memcpy_bytes_length ) ;
return true ;
2017-09-08 16:52:13 +02:00
}
}
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
//1024 height is a hack (for ~720p buffers)
//It is possible to have a large buffer that goes up to around 4kx4k but anything above 1280x720 is rare
//RSX only handles 512x512 tiles so texture 'stitching' will eventually be needed to be completely accurate
//Sections will be submitted as (512x512 + 512x512 + 256x512 + 512x208 + 512x208 + 256x208) to blit a 720p surface to the backbuffer for example
size2i dst_dimensions = { dst . pitch / ( dst_is_argb8 ? 4 : 2 ) , 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 ( ) )
dst_dimensions . height = std : : max ( src_subres . surface - > get_surface_height ( ) , dst . height ) ;
else if ( dst . max_tile_h > dst . height )
dst_dimensions . height = std : : min ( ( s32 ) dst . max_tile_h , 1024 ) ;
}
2017-10-12 14:48:31 +02:00
2017-09-08 16:52:13 +02:00
section_storage_type * cached_dest = nullptr ;
2017-09-14 13:37:14 +02:00
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
2018-09-22 02:14:26 +02:00
auto overlapping_surfaces = find_texture_from_range ( address_range : : start_length ( dst_address , dst . pitch * dst . clip_height ) ) ;
2017-09-19 14:46:16 +02:00
2018-02-10 09:52:44 +01:00
for ( const auto & surface : overlapping_surfaces )
2017-09-08 16:52:13 +02:00
{
2017-09-19 14:46:16 +02:00
if ( surface - > get_context ( ) ! = rsx : : texture_upload_context : : blit_engine_dst )
continue ;
2017-09-08 16:52:13 +02:00
2018-09-22 02:14:26 +02:00
if ( surface - > get_rsx_pitch ( ) ! = dst . pitch )
2018-09-10 12:22:24 +02:00
continue ;
2017-09-19 14:46:16 +02:00
const auto old_dst_area = dst_area ;
if ( const u32 address_offset = dst_address - surface - > get_section_base ( ) )
2017-09-08 16:52:13 +02:00
{
2017-09-19 14:46:16 +02:00
const u16 bpp = dst_is_argb8 ? 4 : 2 ;
const u16 offset_y = address_offset / dst . pitch ;
const u16 offset_x = address_offset % dst . pitch ;
const u16 offset_x_in_block = offset_x / bpp ;
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 ;
break ;
}
2018-09-10 12:22:24 +02:00
dst_area = old_dst_area ;
2017-09-08 16:52:13 +02:00
}
2017-09-18 19:22:34 +02:00
if ( cached_dest )
{
dest_texture = cached_dest - > get_raw_texture ( ) ;
2018-07-26 19:52:22 +02:00
typeless_info . dst_context = cached_dest - > get_context ( ) ;
2017-09-18 19:22:34 +02:00
max_dst_width = cached_dest - > get_width ( ) ;
max_dst_height = cached_dest - > get_height ( ) ;
}
2017-09-08 16:52:13 +02:00
}
else
{
2017-09-18 19:22:34 +02:00
//TODO: Investigate effects of tile compression
dst_area . x1 = dst_subres . x ;
dst_area . y1 = dst_subres . y ;
2017-09-08 16:52:13 +02:00
dst_area . x2 + = dst_subres . x ;
dst_area . y2 + = dst_subres . y ;
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 ( ) ;
}
//Create source texture if does not exist
if ( ! src_is_render_target )
{
2018-09-22 02:14:26 +02:00
auto overlapping_surfaces = find_texture_from_range ( address_range : : start_length ( src_address , src . pitch * src . height ) ) ;
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
{
2017-11-10 13:13:32 +01:00
//look for any that will fit, unless its a shader read surface or framebuffer_storage
if ( surface - > get_context ( ) = = rsx : : texture_upload_context : : shader_read | |
surface - > get_context ( ) = = rsx : : texture_upload_context : : framebuffer_storage )
continue ;
2018-09-22 02:14:26 +02:00
if ( surface - > get_rsx_pitch ( ) ! = src . pitch )
2018-09-10 12:22:24 +02:00
continue ;
2017-11-10 13:13:32 +01:00
if ( const u32 address_offset = src_address - surface - > get_section_base ( ) )
{
2018-09-10 12:22:24 +02:00
const u16 bpp = src_is_argb8 ? 4 : 2 ;
2017-11-10 13:13:32 +01:00
const u16 offset_y = address_offset / src . pitch ;
const u16 offset_x = address_offset % src . pitch ;
const u16 offset_x_in_block = offset_x / bpp ;
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
{
2017-09-14 13:37:14 +02:00
lock . upgrade ( ) ;
2018-12-13 11:23:58 +01:00
const auto rsx_range = address_range : : start_length ( src_address , src . pitch * src . slice_h ) ;
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
const u16 pitch_in_block = src_is_argb8 ? src . pitch > > 2 : src . pitch > > 1 ;
std : : vector < rsx_subresource_layout > subresource_layout ;
rsx_subresource_layout subres = { } ;
subres . width_in_block = src . width ;
subres . height_in_block = src . slice_h ;
2018-04-13 22:59:29 +02:00
subres . pitch_in_block = pitch_in_block ;
2017-09-08 16:52:13 +02:00
subres . depth = 1 ;
subres . data = { ( const gsl : : byte * ) src . pixels , src . pitch * src . slice_h } ;
subresource_layout . push_back ( subres ) ;
const u32 gcm_format = src_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5 ;
2018-12-13 11:23:58 +01:00
vram_texture = upload_image_from_cpu ( cmd , rsx_range , src . width , src . slice_h , 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
{
2017-11-06 22:34:06 +01:00
if ( ! dst_is_render_target )
2017-09-08 16:52:13 +02:00
{
2017-11-06 22:34:06 +01:00
u16 src_subres_w = src_subres . w ;
u16 src_subres_h = src_subres . h ;
get_rsx_dimensions ( src_subres_w , src_subres_h , src_subres . surface ) ;
2018-04-07 17:16:52 +02:00
const int dst_width = ( int ) ( src_subres_w * scale_x * typeless_info . src_scaling_hint ) ;
2017-11-06 22:34:06 +01:00
const int dst_height = ( int ) ( src_subres_h * scale_y ) ;
2017-09-08 16:52:13 +02:00
dst_area . x2 = dst_area . x1 + dst_width ;
dst_area . y2 = dst_area . y1 + dst_height ;
}
src_area . x2 = src_subres . w ;
src_area . y2 = src_subres . h ;
2017-09-18 19:22:34 +02:00
src_area . x1 = src_subres . x ;
src_area . y1 = src_subres . y ;
2017-09-08 16:52:13 +02:00
src_area . x2 + = src_subres . x ;
src_area . y2 + = src_subres . y ;
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
}
2018-04-05 00:00:33 +02:00
const bool src_is_depth = src_subres . is_depth_surface ;
const bool dst_is_depth = dst_is_render_target ? dst_subres . is_depth_surface :
dest_texture ? cached_dest - > is_depth_texture ( ) : src_is_depth ;
2017-09-08 16:52:13 +02:00
2018-04-05 00:00:33 +02:00
//Type of blit decided by the source, destination use should adapt on the fly
const bool is_depth_blit = src_is_depth ;
bool format_mismatch = ( src_is_depth ! = dst_is_depth ) ;
if ( format_mismatch )
2017-09-08 16:52:13 +02:00
{
2018-04-05 00:00:33 +02:00
if ( dst_is_render_target )
2017-09-08 16:52:13 +02:00
{
2018-04-05 00:00:33 +02:00
LOG_ERROR ( RSX , " Depth<->RGBA blit on a framebuffer requested but not supported " ) ;
return false ;
2017-09-08 16:52:13 +02:00
}
2018-04-05 00:00:33 +02:00
}
else if ( src_is_render_target & & cached_dest )
{
2018-04-07 17:16:52 +02:00
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 :
2018-04-05 00:00:33 +02:00
format_mismatch = true ;
2018-04-07 17:16:52 +02:00
break ;
}
2017-09-08 16:52:13 +02:00
}
//TODO: Check for other types of format mismatch
2018-09-22 02:14:26 +02:00
const address_range dst_range = address_range : : start_length ( dst_address , dst . pitch * dst . height ) ;
2018-10-19 00:22:00 +02:00
AUDIT ( cached_dest = = nullptr | | cached_dest - > overlaps ( dst_range , section_bounds : : full_range ) ) ;
2017-09-08 16:52:13 +02:00
if ( format_mismatch )
{
2017-09-14 13:37:14 +02:00
lock . upgrade ( ) ;
2018-09-22 02:14:26 +02:00
// Invalidate as the memory is not reusable now
2018-12-29 14:28:12 +01:00
invalidate_range_impl_base ( cmd , cached_dest - > get_section_range ( ) , invalidation_cause : : write , std : : forward < Args > ( extras ) . . . ) ;
2018-09-22 02:14:26 +02:00
AUDIT ( ! cached_dest - > is_locked ( ) ) ;
2017-09-08 16:52:13 +02:00
dest_texture = 0 ;
cached_dest = nullptr ;
}
2017-12-07 13:08:11 +01:00
u32 gcm_format ;
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 )
{
//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 ) ;
}
2017-09-08 16:52:13 +02:00
//Validate clipping region
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 ;
//Reproject clip offsets onto source to simplify blit
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!
const u32 write_end = dst_address + ( dst . pitch * dst . clip_height ) ;
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
const auto rsx_range = address_range : : start_length ( dst . rsx_address , section_length ) ;
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
const u16 pitch_in_block = dst_is_argb8 ? dst . pitch > > 2 : dst . pitch > > 1 ;
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 ;
subres . data = { ( const gsl : : byte * ) dst . pixels , dst . pitch * dst_dimensions . height } ;
subresource_layout . push_back ( subres ) ;
2018-12-13 11:23:58 +01:00
cached_dest = upload_image_from_cpu ( cmd , rsx_range , dst_dimensions . width , dst_dimensions . height , 1 , 1 , dst . pitch ,
2018-09-18 20:06:34 +02:00
gcm_format , rsx : : texture_upload_context : : blit_engine_dst , subresource_layout ,
rsx : : texture_dimension_extended : : texture_dimension_2d , false ) ;
// 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 ;
2018-09-18 20:06:34 +02:00
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
if ( cached_dest )
2018-02-10 17:21:16 +01:00
{
2018-05-10 13:50:32 +02:00
lock . upgrade ( ) ;
2018-09-10 12:22:24 +02:00
u32 mem_length ;
const u32 mem_base = dst_address - cached_dest - > get_section_base ( ) ;
if ( dst . clip_height = = 1 )
{
mem_length = dst . clip_width * ( dst_is_argb8 ? 4 : 2 ) ;
}
else
{
const u32 mem_excess = mem_base % dst . pitch ;
mem_length = ( dst . pitch * dst . clip_height ) - mem_excess ;
}
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 ;
}
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 ) ;
2017-11-03 12:16:55 +01:00
notify_surface_changed ( dst . rsx_address ) ;
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
} ;
}