rpcs3/rpcs3/Emu/RSX/Common/texture_cache_checker.h

218 lines
No EOL
6.4 KiB
C++

#pragma once
#ifdef TEXTURE_CACHE_DEBUG
#include "../rsx_utils.h"
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;
default: fmt::throw_exception("Unreachable");
}
}
FORCE_INLINE void add(utils::protection prot)
{
switch (prot)
{
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;
default: fmt::throw_exception("Unreachable");
}
}
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;
default: fmt::throw_exception("Unreachable");
}
}
};
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
// Initialized to utils::protection::rw
static constexpr usz num_pages = 0x1'0000'0000 / 4096;
per_page_info_t _info[num_pages]{0};
static_assert(static_cast<u32>(utils::protection::rw) == 0, "utils::protection::rw must have value 0 for the above constructor to work");
static constexpr usz rsx_address_to_index(u32 address)
{
return (address / 4096);
}
static constexpr u32 index_to_rsx_address(usz idx)
{
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
{
return index_to_rsx_address(static_cast<usz>(ptr - _info));
}
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";
default: fmt::throw_exception("Unreachable");
}
}
public:
void set_protection(const address_range32& 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_range32& range)
{
set_protection(range, utils::protection::rw);
}
void reset_refcount()
{
for (per_page_info_t* ptr = rsx_address_to_info_pointer(0); ptr <= rsx_address_to_info_pointer(0xFFFFFFFF); ptr++)
{
ptr->reset_refcount();
}
}
void add(const address_range32& 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_range32& 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_range32& 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_range32& 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
{
for (usz idx = 0; idx < num_pages; idx++)
{
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