2020-12-05 13:08:24 +01:00
# pragma once
2018-09-22 02:14:26 +02:00
# include "../rsx_utils.h"
# ifdef TEXTURE_CACHE_DEBUG
namespace rsx {
class tex_cache_checker_t {
struct per_page_info_t {
u8 prot = 0 ;
u8 no = 0 ;
u8 ro = 0 ;
FORCE_INLINE utils : : protection get_protection ( ) const
{
return static_cast < utils : : protection > ( prot ) ;
}
FORCE_INLINE void set_protection ( utils : : protection prot )
{
this - > prot = static_cast < u8 > ( prot ) ;
}
FORCE_INLINE void reset_refcount ( )
{
no = 0 ;
ro = 0 ;
}
FORCE_INLINE u16 sum ( ) const
{
return u16 { no } + ro ;
}
FORCE_INLINE bool verify ( ) const
{
const utils : : protection prot = get_protection ( ) ;
switch ( prot )
{
case utils : : protection : : no : return no > 0 ;
case utils : : protection : : ro : return no = = 0 & & ro > 0 ;
case utils : : protection : : rw : return no = = 0 & & ro = = 0 ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2018-09-22 02:14:26 +02:00
}
}
FORCE_INLINE void add ( utils : : protection prot )
{
switch ( prot )
{
2021-05-22 20:46:10 +02:00
case utils : : protection : : no : if ( no + + = = umax ) fmt : : throw_exception ( " add(protection::no) overflow " ) ; return ;
case utils : : protection : : ro : if ( ro + + = = umax ) fmt : : throw_exception ( " add(protection::ro) overflow " ) ; return ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2018-09-22 02:14:26 +02:00
}
}
FORCE_INLINE void remove ( utils : : protection prot )
{
switch ( prot )
{
case utils : : protection : : no : if ( no - - = = 0 ) fmt : : throw_exception ( " remove(protection::no) overflow with NO==0 " ) ; return ;
case utils : : protection : : ro : if ( ro - - = = 0 ) fmt : : throw_exception ( " remove(protection::ro) overflow with RO==0 " ) ; return ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2018-09-22 02:14:26 +02:00
}
}
} ;
static_assert ( sizeof ( per_page_info_t ) < = 4 , " page_info_elmnt must be less than 4-bytes in size " ) ;
// 4GB memory space / 4096 bytes per page = 1048576 pages
2019-12-20 04:51:16 +01:00
// Initialized to utils::protection::rw
2020-12-18 08:39:54 +01:00
static constexpr usz num_pages = 0x1 ' 0000 ' 0000 / 4096 ;
2019-12-20 04:51:16 +01:00
per_page_info_t _info [ num_pages ] { 0 } ;
2020-12-09 08:47:45 +01:00
2019-12-20 04:51:16 +01:00
static_assert ( static_cast < u32 > ( utils : : protection : : rw ) = = 0 , " utils::protection::rw must have value 0 for the above constructor to work " ) ;
2018-09-22 02:14:26 +02:00
2020-12-18 08:39:54 +01:00
static constexpr usz rsx_address_to_index ( u32 address )
2018-09-22 02:14:26 +02:00
{
return ( address / 4096 ) ;
}
2020-12-18 08:39:54 +01:00
static constexpr u32 index_to_rsx_address ( usz idx )
2018-09-22 02:14:26 +02:00
{
return static_cast < u32 > ( idx * 4096 ) ;
}
constexpr per_page_info_t * rsx_address_to_info_pointer ( u32 address )
{
return & ( _info [ rsx_address_to_index ( address ) ] ) ;
}
constexpr const per_page_info_t * rsx_address_to_info_pointer ( u32 address ) const
{
return & ( _info [ rsx_address_to_index ( address ) ] ) ;
}
constexpr u32 info_pointer_to_address ( const per_page_info_t * ptr ) const
{
2020-12-18 08:39:54 +01:00
return index_to_rsx_address ( static_cast < usz > ( ptr - _info ) ) ;
2018-09-22 02:14:26 +02:00
}
std : : string prot_to_str ( utils : : protection prot ) const
{
switch ( prot )
{
case utils : : protection : : no : return " NA " ;
case utils : : protection : : ro : return " RO " ;
case utils : : protection : : rw : return " RW " ;
2020-12-09 16:04:52 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2018-09-22 02:14:26 +02:00
}
}
public :
void set_protection ( const address_range & range , utils : : protection prot )
{
AUDIT ( range . is_page_range ( ) ) ;
AUDIT ( prot = = utils : : protection : : no | | prot = = utils : : protection : : ro | | prot = = utils : : protection : : rw ) ;
for ( per_page_info_t * ptr = rsx_address_to_info_pointer ( range . start ) ; ptr < = rsx_address_to_info_pointer ( range . end ) ; ptr + + )
{
ptr - > set_protection ( prot ) ;
}
}
void discard ( const address_range & range )
{
set_protection ( range , utils : : protection : : rw ) ;
}
void reset_refcount ( )
{
2020-04-06 19:25:57 +02:00
for ( per_page_info_t * ptr = rsx_address_to_info_pointer ( 0 ) ; ptr < = rsx_address_to_info_pointer ( 0xFFFFFFFF ) ; ptr + + )
2018-09-22 02:14:26 +02:00
{
ptr - > reset_refcount ( ) ;
}
}
void add ( const address_range & range , utils : : protection prot )
{
AUDIT ( range . is_page_range ( ) ) ;
AUDIT ( prot = = utils : : protection : : no | | prot = = utils : : protection : : ro ) ;
for ( per_page_info_t * ptr = rsx_address_to_info_pointer ( range . start ) ; ptr < = rsx_address_to_info_pointer ( range . end ) ; ptr + + )
{
ptr - > add ( prot ) ;
}
}
void remove ( const address_range & range , utils : : protection prot )
{
AUDIT ( range . is_page_range ( ) ) ;
AUDIT ( prot = = utils : : protection : : no | | prot = = utils : : protection : : ro ) ;
for ( per_page_info_t * ptr = rsx_address_to_info_pointer ( range . start ) ; ptr < = rsx_address_to_info_pointer ( range . end ) ; ptr + + )
{
ptr - > remove ( prot ) ;
}
}
// Returns the a lower bound as to how many locked sections are known to be within the given range with each protection {NA,RO}
// The assumption here is that the page in the given range with the largest number of refcounted sections represents the lower bound to how many there must be
std : : pair < u8 , u8 > get_minimum_number_of_sections ( const address_range & range ) const
{
AUDIT ( range . is_page_range ( ) ) ;
u8 no = 0 ;
u8 ro = 0 ;
for ( const per_page_info_t * ptr = rsx_address_to_info_pointer ( range . start ) ; ptr < = rsx_address_to_info_pointer ( range . end ) ; ptr + + )
{
no = std : : max ( no , ptr - > no ) ;
ro = std : : max ( ro , ptr - > ro ) ;
}
return { no , ro } ;
}
void check_unprotected ( const address_range & range , bool allow_ro = false , bool must_be_empty = true ) const
{
AUDIT ( range . is_page_range ( ) ) ;
for ( const per_page_info_t * ptr = rsx_address_to_info_pointer ( range . start ) ; ptr < = rsx_address_to_info_pointer ( range . end ) ; ptr + + )
{
const auto prot = ptr - > get_protection ( ) ;
if ( prot ! = utils : : protection : : rw & & ( ! allow_ro | | prot ! = utils : : protection : : ro ) )
{
const u32 addr = info_pointer_to_address ( ptr ) ;
fmt : : throw_exception ( " Page at addr=0x%8x should be RW%s: Prot=%s, RO=%d, NA=%d " , addr , allow_ro ? " or RO " : " " , prot_to_str ( prot ) , ptr - > ro , ptr - > no ) ;
}
if ( must_be_empty & & (
ptr - > no > 0 | |
( ! allow_ro & & ptr - > ro > 0 )
) )
{
const u32 addr = info_pointer_to_address ( ptr ) ;
fmt : : throw_exception ( " Page at addr=0x%8x should not have any NA%s sections: Prot=%s, RO=%d, NA=%d " , addr , allow_ro ? " or RO " : " " , prot_to_str ( prot ) , ptr - > ro , ptr - > no ) ;
}
}
}
void verify ( ) const
{
2020-12-18 08:39:54 +01:00
for ( usz idx = 0 ; idx < num_pages ; idx + + )
2018-09-22 02:14:26 +02:00
{
auto & elmnt = _info [ idx ] ;
if ( ! elmnt . verify ( ) )
{
const u32 addr = index_to_rsx_address ( idx ) ;
const utils : : protection prot = elmnt . get_protection ( ) ;
fmt : : throw_exception ( " Protection verification failed at addr=0x%x: Prot=%s, RO=%d, NA=%d " , addr , prot_to_str ( prot ) , elmnt . ro , elmnt . no ) ;
}
}
}
} ;
extern tex_cache_checker_t tex_cache_checker ;
} ; // namespace rsx
# endif //TEXTURE_CACHE_DEBUG