mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
rsx: Refactor texture cache utils
- Also lays groundwork for optional hashed sections
This commit is contained in:
parent
8d6504d6e3
commit
1bad9a939f
@ -4,23 +4,33 @@
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
constexpr usz fnv_seed = 14695981039346656037ull;
|
||||
constexpr usz fnv_prime = 1099511628211ull;
|
||||
|
||||
template<typename T>
|
||||
static usz hash_base(T value)
|
||||
{
|
||||
return static_cast<usz>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value, bool>>
|
||||
static inline usz hash64(usz hash_value, const T data)
|
||||
{
|
||||
hash_value ^= data;
|
||||
hash_value *= fnv_prime;
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static usz hash_struct_base(const T& value)
|
||||
{
|
||||
// FNV 64-bit
|
||||
usz result = 14695981039346656037ull;
|
||||
usz result = fnv_seed;
|
||||
const U *bits = reinterpret_cast<const U*>(&value);
|
||||
|
||||
for (usz n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||
{
|
||||
result ^= bits[n];
|
||||
result *= 1099511628211ull;
|
||||
result = hash64(result, bits[n]);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -406,6 +406,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Common/ProgramStateCache.cpp
|
||||
RSX/Common/surface_store.cpp
|
||||
RSX/Common/TextureUtils.cpp
|
||||
RSX/Common/texture_cache.cpp
|
||||
RSX/Common/VertexProgramDecompiler.cpp
|
||||
RSX/Null/NullGSRender.cpp
|
||||
RSX/Overlays/overlay_animation.cpp
|
||||
|
213
rpcs3/Emu/RSX/Common/texture_cache.cpp
Normal file
213
rpcs3/Emu/RSX/Common/texture_cache.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include "stdafx.h"
|
||||
#include "texture_cache_utils.h"
|
||||
#include "Utilities/address_range.h"
|
||||
#include "Utilities/hash.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
void buffered_section::init_lockable_range(const address_range& range)
|
||||
{
|
||||
locked_range = range.to_page_range();
|
||||
AUDIT((locked_range.start == page_start(range.start)) || (locked_range.start == next_page(range.start)));
|
||||
AUDIT(locked_range.end <= page_end(range.end));
|
||||
ensure(locked_range.is_page_range());
|
||||
}
|
||||
|
||||
void buffered_section::reset(const address_range& memory_range)
|
||||
{
|
||||
ensure(memory_range.valid() && locked == false);
|
||||
|
||||
cpu_range = address_range(memory_range);
|
||||
confirmed_range.invalidate();
|
||||
locked_range.invalidate();
|
||||
|
||||
protection = utils::protection::rw;
|
||||
protection_strat = section_protection_strategy::lock;
|
||||
locked = false;
|
||||
|
||||
init_lockable_range(cpu_range);
|
||||
|
||||
if (memory_range.length() < 4096)
|
||||
{
|
||||
protection_strat = section_protection_strategy::hash;
|
||||
mem_hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void buffered_section::invalidate_range()
|
||||
{
|
||||
ensure(!locked);
|
||||
|
||||
cpu_range.invalidate();
|
||||
confirmed_range.invalidate();
|
||||
locked_range.invalidate();
|
||||
}
|
||||
|
||||
void buffered_section::protect(utils::protection new_prot, bool force)
|
||||
{
|
||||
if (new_prot == protection && !force) return;
|
||||
|
||||
ensure(locked_range.is_page_range());
|
||||
AUDIT(!confirmed_range.valid() || confirmed_range.inside(cpu_range));
|
||||
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
if (new_prot != protection || force)
|
||||
{
|
||||
if (locked && !force) // When force=true, it is the responsibility of the caller to remove this section from the checker refcounting
|
||||
tex_cache_checker.remove(locked_range, protection);
|
||||
if (new_prot != utils::protection::rw)
|
||||
tex_cache_checker.add(locked_range, new_prot);
|
||||
}
|
||||
#endif // TEXTURE_CACHE_DEBUG
|
||||
|
||||
if (new_prot == utils::protection::no)
|
||||
{
|
||||
// Override
|
||||
protection_strat = section_protection_strategy::lock;
|
||||
}
|
||||
|
||||
if (protection_strat == section_protection_strategy::lock)
|
||||
{
|
||||
rsx::memory_protect(locked_range, new_prot);
|
||||
}
|
||||
else if (new_prot != utils::protection::rw)
|
||||
{
|
||||
mem_hash = fast_hash_internal();
|
||||
}
|
||||
|
||||
protection = new_prot;
|
||||
locked = (protection != utils::protection::rw);
|
||||
|
||||
if (!locked)
|
||||
{
|
||||
// Unprotect range also invalidates secured range
|
||||
confirmed_range.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void buffered_section::protect(utils::protection prot, const std::pair<u32, u32>& new_confirm)
|
||||
{
|
||||
// new_confirm.first is an offset after cpu_range.start
|
||||
// new_confirm.second is the length (after cpu_range.start + new_confirm.first)
|
||||
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
// We need to remove the lockable range from page_info as we will be re-protecting with force==true
|
||||
if (locked)
|
||||
tex_cache_checker.remove(locked_range, protection);
|
||||
#endif
|
||||
|
||||
if (prot != utils::protection::rw)
|
||||
{
|
||||
if (confirmed_range.valid())
|
||||
{
|
||||
confirmed_range.start = std::min(confirmed_range.start, cpu_range.start + new_confirm.first);
|
||||
confirmed_range.end = std::max(confirmed_range.end, cpu_range.start + new_confirm.first + new_confirm.second - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
confirmed_range = address_range::start_length(cpu_range.start + new_confirm.first, new_confirm.second);
|
||||
ensure(!locked || locked_range.inside(confirmed_range.to_page_range()));
|
||||
}
|
||||
|
||||
ensure(confirmed_range.inside(cpu_range));
|
||||
init_lockable_range(confirmed_range);
|
||||
}
|
||||
|
||||
protect(prot, true);
|
||||
}
|
||||
|
||||
void buffered_section::unprotect()
|
||||
{
|
||||
AUDIT(protection != utils::protection::rw);
|
||||
protect(utils::protection::rw);
|
||||
}
|
||||
|
||||
void buffered_section::discard()
|
||||
{
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
if (locked)
|
||||
tex_cache_checker.remove(locked_range, protection);
|
||||
#endif
|
||||
|
||||
protection = utils::protection::rw;
|
||||
confirmed_range.invalidate();
|
||||
locked = false;
|
||||
}
|
||||
|
||||
const address_range& buffered_section::get_bounds(section_bounds bounds) const
|
||||
{
|
||||
switch (bounds)
|
||||
{
|
||||
case section_bounds::full_range:
|
||||
return cpu_range;
|
||||
case section_bounds::locked_range:
|
||||
return locked_range;
|
||||
case section_bounds::confirmed_range:
|
||||
return confirmed_range.valid() ? confirmed_range : cpu_range;
|
||||
default:
|
||||
fmt::throw_exception("Unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
u64 buffered_section::fast_hash_internal() const
|
||||
{
|
||||
const auto hash_range = confirmed_range.valid() ? confirmed_range : cpu_range;
|
||||
const auto hash_length = hash_range.length();
|
||||
const auto cycles = hash_length / 8;
|
||||
auto rem = hash_length % 8;
|
||||
|
||||
auto src = get_ptr<const char>(hash_range.start);
|
||||
auto data64 = reinterpret_cast<const u64*>(src);
|
||||
|
||||
usz hash = rpcs3::fnv_seed;
|
||||
for (unsigned i = 0; i < cycles; ++i)
|
||||
{
|
||||
hash = rpcs3::hash64(hash, data64[i]);
|
||||
}
|
||||
|
||||
if (rem) [[unlikely]] // Data often aligned to some power of 2
|
||||
{
|
||||
src += hash_length - rem;
|
||||
|
||||
if (rem > 4)
|
||||
{
|
||||
hash = rpcs3::hash64(hash, *reinterpret_cast<const u32*>(src));
|
||||
src += 4;
|
||||
}
|
||||
|
||||
if (rem > 2)
|
||||
{
|
||||
hash = rpcs3::hash64(hash, *reinterpret_cast<const u16*>(src));
|
||||
src += 2;
|
||||
}
|
||||
|
||||
while (rem--)
|
||||
{
|
||||
hash = rpcs3::hash64(hash, *reinterpret_cast<const u8*>(src));
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool buffered_section::is_locked(bool actual_page_flags) const
|
||||
{
|
||||
if (!actual_page_flags || !locked)
|
||||
{
|
||||
return locked;
|
||||
}
|
||||
|
||||
return (protection_strat == section_protection_strategy::lock);
|
||||
}
|
||||
|
||||
bool buffered_section::sync()
|
||||
{
|
||||
if (protection_strat == section_protection_strategy::lock || !locked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return (fast_hash_internal() == mem_hash);
|
||||
}
|
||||
}
|
@ -670,11 +670,7 @@ namespace rsx
|
||||
u32 last_dirty_block = UINT32_MAX;
|
||||
bool repeat_loop = false;
|
||||
|
||||
// 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;
|
||||
|
||||
auto It = m_storage.range_begin(invalidate_range, range_it_bounds, true); // will iterate through locked sections only
|
||||
auto It = m_storage.range_begin(invalidate_range, locked_range, true); // will iterate through locked sections only
|
||||
while (It != m_storage.range_end())
|
||||
{
|
||||
const u32 base = It.get_block().get_start();
|
||||
@ -690,9 +686,16 @@ namespace rsx
|
||||
|
||||
if (tex.cache_tag != cache_tag) //flushable sections can be 'clean' but unlocked. TODO: Handle this better
|
||||
{
|
||||
tex.sync_protection();
|
||||
if (!tex.is_locked())
|
||||
{
|
||||
It++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const rsx::section_bounds bounds = tex.get_overlap_test_bounds();
|
||||
|
||||
if (range_it_bounds == bounds || tex.overlaps(invalidate_range, bounds))
|
||||
if (locked_range == bounds || tex.overlaps(invalidate_range, bounds))
|
||||
{
|
||||
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));
|
||||
@ -728,7 +731,7 @@ namespace rsx
|
||||
// repeat_loop==true means some blocks are still dirty and we need to repeat the loop again
|
||||
if (repeat_loop && It == m_storage.range_end())
|
||||
{
|
||||
It = m_storage.range_begin(invalidate_range, range_it_bounds, true);
|
||||
It = m_storage.range_begin(invalidate_range, locked_range, true);
|
||||
repeat_loop = false;
|
||||
}
|
||||
}
|
||||
@ -863,7 +866,7 @@ namespace rsx
|
||||
// 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
|
||||
if (tex.is_dirty() || !tex.test_memory_head() || !tex.test_memory_tail())
|
||||
if (tex.is_dirty())
|
||||
{
|
||||
// Contents clobbered, destroy this
|
||||
if (!tex.is_dirty())
|
||||
@ -973,18 +976,17 @@ namespace rsx
|
||||
{
|
||||
std::vector<section_storage_type*> results;
|
||||
|
||||
for (auto It = m_storage.range_begin(test_range, full_range); It != m_storage.range_end(); It++)
|
||||
for (auto It = m_storage.range_begin(test_range, full_range, check_unlocked); It != m_storage.range_end(); It++)
|
||||
{
|
||||
auto &tex = *It;
|
||||
|
||||
if constexpr (check_unlocked)
|
||||
{
|
||||
tex.sync_protection();
|
||||
}
|
||||
|
||||
if (!tex.is_dirty() && (context_mask & static_cast<u32>(tex.get_context())))
|
||||
{
|
||||
if constexpr (check_unlocked)
|
||||
{
|
||||
if (!tex.is_locked())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (required_pitch && !rsx::pitch_compatible<false>(&tex, required_pitch, UINT16_MAX))
|
||||
{
|
||||
continue;
|
||||
@ -1393,6 +1395,13 @@ namespace rsx
|
||||
return false;
|
||||
}
|
||||
|
||||
void trim_sections()
|
||||
{
|
||||
std::lock_guard lock(m_cache_mutex);
|
||||
|
||||
m_storage.trim_sections();
|
||||
}
|
||||
|
||||
image_view_type create_temporary_subresource(commandbuffer_type &cmd, deferred_subresource& desc)
|
||||
{
|
||||
if (!desc.do_not_cache) [[likely]]
|
||||
|
@ -10,6 +10,31 @@
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
enum section_bounds
|
||||
{
|
||||
full_range,
|
||||
locked_range,
|
||||
confirmed_range
|
||||
};
|
||||
|
||||
enum section_protection_strategy
|
||||
{
|
||||
lock,
|
||||
hash
|
||||
};
|
||||
|
||||
static inline void memory_protect(const address_range& range, utils::protection prot)
|
||||
{
|
||||
ensure(range.is_page_range());
|
||||
|
||||
//rsx_log.error("memory_protect(0x%x, 0x%x, %x)", static_cast<u32>(range.start), static_cast<u32>(range.length()), static_cast<u32>(prot));
|
||||
utils::memory_protect(vm::base(range.start), range.length(), prot);
|
||||
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
tex_cache_checker.set_protection(range, prot);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* List structure used in Ranged Storage Blocks
|
||||
* List of Arrays
|
||||
@ -603,6 +628,24 @@ namespace rsx
|
||||
return any_released;
|
||||
}
|
||||
|
||||
void trim_sections()
|
||||
{
|
||||
for (auto it = m_in_use.begin(); it != m_in_use.end(); it++)
|
||||
{
|
||||
auto* block = *it;
|
||||
if (block->get_locked_count() > 256)
|
||||
{
|
||||
for (auto& tex : *block)
|
||||
{
|
||||
if (tex.is_locked() && !tex.is_locked(true))
|
||||
{
|
||||
tex.sync_protection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callbacks
|
||||
@ -854,7 +897,157 @@ namespace rsx
|
||||
|
||||
};
|
||||
|
||||
class buffered_section
|
||||
{
|
||||
private:
|
||||
address_range locked_range;
|
||||
address_range cpu_range = {};
|
||||
address_range confirmed_range;
|
||||
|
||||
utils::protection protection = utils::protection::rw;
|
||||
|
||||
section_protection_strategy protection_strat = section_protection_strategy::lock;
|
||||
u64 mem_hash = 0;
|
||||
|
||||
bool locked = false;
|
||||
void init_lockable_range(const address_range& range);
|
||||
u64 fast_hash_internal() const;
|
||||
|
||||
public:
|
||||
|
||||
buffered_section() = default;
|
||||
~buffered_section() = default;
|
||||
|
||||
void reset(const address_range& memory_range);
|
||||
|
||||
protected:
|
||||
void invalidate_range();
|
||||
|
||||
public:
|
||||
void protect(utils::protection new_prot, bool force = false);
|
||||
void protect(utils::protection prot, const std::pair<u32, u32>& new_confirm);
|
||||
void unprotect();
|
||||
bool sync();
|
||||
|
||||
void discard();
|
||||
const address_range& get_bounds(section_bounds bounds) const;
|
||||
|
||||
bool is_locked(bool actual_page_flags = false) const;
|
||||
|
||||
/**
|
||||
* Overlapping checks
|
||||
*/
|
||||
inline bool overlaps(const u32 address, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(address);
|
||||
}
|
||||
|
||||
inline bool overlaps(const address_range& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(other);
|
||||
}
|
||||
|
||||
inline bool overlaps(const address_range_vector& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(other);
|
||||
}
|
||||
|
||||
inline bool overlaps(const buffered_section& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(other.get_bounds(bounds));
|
||||
}
|
||||
|
||||
inline bool inside(const address_range& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).inside(other);
|
||||
}
|
||||
|
||||
inline bool inside(const address_range_vector& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).inside(other);
|
||||
}
|
||||
|
||||
inline bool inside(const buffered_section& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).inside(other.get_bounds(bounds));
|
||||
}
|
||||
|
||||
inline s32 signed_distance(const address_range& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).signed_distance(other);
|
||||
}
|
||||
|
||||
inline u32 distance(const address_range& other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).distance(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
inline bool valid_range() const
|
||||
{
|
||||
return cpu_range.valid();
|
||||
}
|
||||
|
||||
inline u32 get_section_base() const
|
||||
{
|
||||
return cpu_range.start;
|
||||
}
|
||||
|
||||
inline u32 get_section_size() const
|
||||
{
|
||||
return cpu_range.valid() ? cpu_range.length() : 0;
|
||||
}
|
||||
|
||||
inline const address_range& get_locked_range() const
|
||||
{
|
||||
AUDIT(locked);
|
||||
return locked_range;
|
||||
}
|
||||
|
||||
inline const address_range& get_section_range() const
|
||||
{
|
||||
return cpu_range;
|
||||
}
|
||||
|
||||
const address_range& get_confirmed_range() const
|
||||
{
|
||||
return confirmed_range.valid() ? confirmed_range : cpu_range;
|
||||
}
|
||||
|
||||
const std::pair<u32, u32> get_confirmed_range_delta() const
|
||||
{
|
||||
if (!confirmed_range.valid())
|
||||
return { 0, cpu_range.length() };
|
||||
|
||||
return { confirmed_range.start - cpu_range.start, confirmed_range.length() };
|
||||
}
|
||||
|
||||
inline bool matches(const address_range& range) const
|
||||
{
|
||||
return cpu_range.valid() && cpu_range == range;
|
||||
}
|
||||
|
||||
inline utils::protection get_protection() const
|
||||
{
|
||||
return protection;
|
||||
}
|
||||
|
||||
inline address_range get_min_max(const address_range& current_min_max, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).get_min_max(current_min_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Super Pointer
|
||||
*/
|
||||
template <typename T = void>
|
||||
inline T* get_ptr(u32 address) const
|
||||
{
|
||||
return reinterpret_cast<T*>(vm::g_sudo_addr + address);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cached Texture Section
|
||||
@ -1287,6 +1480,15 @@ namespace rsx
|
||||
flush_exclusions.clear();
|
||||
}
|
||||
|
||||
void sync_protection()
|
||||
{
|
||||
if (!buffered_section::sync())
|
||||
{
|
||||
discard(true);
|
||||
ensure(is_dirty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flush
|
||||
@ -1522,13 +1724,7 @@ namespace rsx
|
||||
|
||||
rsx::section_bounds get_overlap_test_bounds() const
|
||||
{
|
||||
if (guard_policy == protection_policy::protect_policy_full_range)
|
||||
return rsx::section_bounds::locked_range;
|
||||
|
||||
const bool strict_range_check = g_cfg.video.write_color_buffers || g_cfg.video.write_depth_buffer;
|
||||
return (strict_range_check || get_context() == rsx::texture_upload_context::blit_engine_dst) ?
|
||||
rsx::section_bounds::confirmed_range :
|
||||
rsx::section_bounds::locked_range;
|
||||
return rsx::section_bounds::locked_range;
|
||||
}
|
||||
|
||||
rsx::texture_dimension_extended get_image_type() const
|
||||
|
@ -873,6 +873,8 @@ namespace gl
|
||||
|
||||
void on_frame_end() override
|
||||
{
|
||||
trim_sections();
|
||||
|
||||
if (m_storage.m_unreleased_texture_objects >= m_max_zombie_objects)
|
||||
{
|
||||
purge_unreleased_sections();
|
||||
|
@ -1098,6 +1098,8 @@ namespace vk
|
||||
|
||||
void on_frame_end() override
|
||||
{
|
||||
trim_sections();
|
||||
|
||||
if (m_storage.m_unreleased_texture_objects >= m_max_zombie_objects ||
|
||||
m_temporary_memory_size > 0x4000000) //If already holding over 64M in discardable memory, be frugal with memory resources
|
||||
{
|
||||
|
@ -20,373 +20,6 @@
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
enum protection_policy
|
||||
{
|
||||
protect_policy_one_page, //Only guard one page, preferably one where this section 'wholly' fits
|
||||
protect_policy_conservative, //Guards as much memory as possible that is guaranteed to only be covered by the defined range without sharing
|
||||
protect_policy_full_range //Guard the full memory range. Shared pages may be invalidated by access outside the object we're guarding
|
||||
};
|
||||
|
||||
enum section_bounds
|
||||
{
|
||||
full_range,
|
||||
locked_range,
|
||||
confirmed_range
|
||||
};
|
||||
|
||||
static inline void memory_protect(const address_range& range, utils::protection prot)
|
||||
{
|
||||
ensure(range.is_page_range());
|
||||
|
||||
//rsx_log.error("memory_protect(0x%x, 0x%x, %x)", static_cast<u32>(range.start), static_cast<u32>(range.length()), static_cast<u32>(prot));
|
||||
utils::memory_protect(vm::base(range.start), range.length(), prot);
|
||||
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
tex_cache_checker.set_protection(range, prot);
|
||||
#endif
|
||||
}
|
||||
|
||||
class buffered_section
|
||||
{
|
||||
public:
|
||||
static const protection_policy guard_policy = protect_policy_full_range;
|
||||
|
||||
private:
|
||||
address_range locked_range;
|
||||
address_range cpu_range = {};
|
||||
address_range confirmed_range;
|
||||
|
||||
utils::protection protection = utils::protection::rw;
|
||||
|
||||
bool locked = false;
|
||||
|
||||
inline void init_lockable_range(const address_range &range)
|
||||
{
|
||||
locked_range = range.to_page_range();
|
||||
|
||||
if ((guard_policy != protect_policy_full_range) && (range.length() >= 4096))
|
||||
{
|
||||
const u32 block_start = (locked_range.start < range.start) ? (locked_range.start + 4096u) : locked_range.start;
|
||||
const u32 block_end = locked_range.end;
|
||||
|
||||
if (block_start < block_end)
|
||||
{
|
||||
// protect unique page range
|
||||
locked_range.start = block_start;
|
||||
locked_range.end = block_end;
|
||||
}
|
||||
|
||||
if (guard_policy == protect_policy_one_page)
|
||||
{
|
||||
// protect exactly one page
|
||||
locked_range.set_length(4096u);
|
||||
}
|
||||
}
|
||||
|
||||
AUDIT( (locked_range.start == page_start(range.start)) || (locked_range.start == next_page(range.start)) );
|
||||
AUDIT( locked_range.end <= page_end(range.end) );
|
||||
ensure(locked_range.is_page_range());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
buffered_section() = default;
|
||||
~buffered_section() = default;
|
||||
|
||||
void reset(const address_range &memory_range)
|
||||
{
|
||||
ensure(memory_range.valid() && locked == false);
|
||||
|
||||
cpu_range = address_range(memory_range);
|
||||
confirmed_range.invalidate();
|
||||
locked_range.invalidate();
|
||||
|
||||
protection = utils::protection::rw;
|
||||
locked = false;
|
||||
|
||||
init_lockable_range(cpu_range);
|
||||
}
|
||||
|
||||
protected:
|
||||
void invalidate_range()
|
||||
{
|
||||
ensure(!locked);
|
||||
|
||||
cpu_range.invalidate();
|
||||
confirmed_range.invalidate();
|
||||
locked_range.invalidate();
|
||||
}
|
||||
|
||||
public:
|
||||
void protect(utils::protection new_prot, bool force = false)
|
||||
{
|
||||
if (new_prot == protection && !force) return;
|
||||
|
||||
ensure(locked_range.is_page_range());
|
||||
AUDIT( !confirmed_range.valid() || confirmed_range.inside(cpu_range) );
|
||||
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
if (new_prot != protection || force)
|
||||
{
|
||||
if (locked && !force) // When force=true, it is the responsibility of the caller to remove this section from the checker refcounting
|
||||
tex_cache_checker.remove(locked_range, protection);
|
||||
if (new_prot != utils::protection::rw)
|
||||
tex_cache_checker.add(locked_range, new_prot);
|
||||
}
|
||||
#endif // TEXTURE_CACHE_DEBUG
|
||||
|
||||
rsx::memory_protect(locked_range, new_prot);
|
||||
protection = new_prot;
|
||||
locked = (protection != utils::protection::rw);
|
||||
|
||||
if (protection == utils::protection::no)
|
||||
{
|
||||
tag_memory();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!locked)
|
||||
{
|
||||
//Unprotect range also invalidates secured range
|
||||
confirmed_range.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void protect(utils::protection prot, const std::pair<u32, u32>& new_confirm)
|
||||
{
|
||||
// new_confirm.first is an offset after cpu_range.start
|
||||
// new_confirm.second is the length (after cpu_range.start + new_confirm.first)
|
||||
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
// We need to remove the lockable range from page_info as we will be re-protecting with force==true
|
||||
if (locked)
|
||||
tex_cache_checker.remove(locked_range, protection);
|
||||
#endif
|
||||
|
||||
if (prot != utils::protection::rw)
|
||||
{
|
||||
if (confirmed_range.valid())
|
||||
{
|
||||
confirmed_range.start = std::min(confirmed_range.start, cpu_range.start + new_confirm.first);
|
||||
confirmed_range.end = std::max(confirmed_range.end, cpu_range.start + new_confirm.first + new_confirm.second - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
confirmed_range = address_range::start_length(cpu_range.start + new_confirm.first, new_confirm.second);
|
||||
ensure(!locked || locked_range.inside(confirmed_range.to_page_range()));
|
||||
}
|
||||
|
||||
ensure(confirmed_range.inside(cpu_range));
|
||||
init_lockable_range(confirmed_range);
|
||||
}
|
||||
|
||||
protect(prot, true);
|
||||
}
|
||||
|
||||
inline void unprotect()
|
||||
{
|
||||
AUDIT(protection != utils::protection::rw);
|
||||
protect(utils::protection::rw);
|
||||
}
|
||||
|
||||
inline void discard()
|
||||
{
|
||||
#ifdef TEXTURE_CACHE_DEBUG
|
||||
if (locked)
|
||||
tex_cache_checker.remove(locked_range, protection);
|
||||
#endif
|
||||
|
||||
protection = utils::protection::rw;
|
||||
confirmed_range.invalidate();
|
||||
locked = false;
|
||||
}
|
||||
|
||||
inline const address_range& get_bounds(section_bounds bounds) const
|
||||
{
|
||||
switch (bounds)
|
||||
{
|
||||
case section_bounds::full_range:
|
||||
return cpu_range;
|
||||
case section_bounds::locked_range:
|
||||
return locked_range;
|
||||
case section_bounds::confirmed_range:
|
||||
return confirmed_range.valid() ? confirmed_range : cpu_range;
|
||||
default:
|
||||
fmt::throw_exception("Unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overlapping checks
|
||||
*/
|
||||
inline bool overlaps(const u32 address, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(address);
|
||||
}
|
||||
|
||||
inline bool overlaps(const address_range &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(other);
|
||||
}
|
||||
|
||||
inline bool overlaps(const address_range_vector &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(other);
|
||||
}
|
||||
|
||||
inline bool overlaps(const buffered_section &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).overlaps(other.get_bounds(bounds));
|
||||
}
|
||||
|
||||
inline bool inside(const address_range &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).inside(other);
|
||||
}
|
||||
|
||||
inline bool inside(const address_range_vector &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).inside(other);
|
||||
}
|
||||
|
||||
inline bool inside(const buffered_section &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).inside(other.get_bounds(bounds));
|
||||
}
|
||||
|
||||
inline s32 signed_distance(const address_range &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).signed_distance(other);
|
||||
}
|
||||
|
||||
inline u32 distance(const address_range &other, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).distance(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
inline bool valid_range() const
|
||||
{
|
||||
return cpu_range.valid();
|
||||
}
|
||||
|
||||
inline bool is_locked() const
|
||||
{
|
||||
return locked;
|
||||
}
|
||||
|
||||
inline u32 get_section_base() const
|
||||
{
|
||||
return cpu_range.start;
|
||||
}
|
||||
|
||||
inline u32 get_section_size() const
|
||||
{
|
||||
return cpu_range.valid() ? cpu_range.length() : 0;
|
||||
}
|
||||
|
||||
inline const address_range& get_locked_range() const
|
||||
{
|
||||
AUDIT( locked );
|
||||
return locked_range;
|
||||
}
|
||||
|
||||
inline const address_range& get_section_range() const
|
||||
{
|
||||
return cpu_range;
|
||||
}
|
||||
|
||||
const address_range& get_confirmed_range() const
|
||||
{
|
||||
return confirmed_range.valid() ? confirmed_range : cpu_range;
|
||||
}
|
||||
|
||||
const std::pair<u32, u32> get_confirmed_range_delta() const
|
||||
{
|
||||
if (!confirmed_range.valid())
|
||||
return { 0, cpu_range.length() };
|
||||
|
||||
return { confirmed_range.start - cpu_range.start, confirmed_range.length() };
|
||||
}
|
||||
|
||||
inline bool matches(const address_range &range) const
|
||||
{
|
||||
return cpu_range.valid() && cpu_range == range;
|
||||
}
|
||||
|
||||
inline utils::protection get_protection() const
|
||||
{
|
||||
return protection;
|
||||
}
|
||||
|
||||
inline address_range get_min_max(const address_range& current_min_max, section_bounds bounds) const
|
||||
{
|
||||
return get_bounds(bounds).get_min_max(current_min_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Super Pointer
|
||||
*/
|
||||
template <typename T = void>
|
||||
inline T* get_ptr(u32 address) const
|
||||
{
|
||||
return reinterpret_cast<T*>(vm::g_sudo_addr + address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory tagging
|
||||
*/
|
||||
private:
|
||||
inline void tag_memory()
|
||||
{
|
||||
// We only need to tag memory if we are in full-range mode
|
||||
if (guard_policy == protect_policy_full_range)
|
||||
return;
|
||||
|
||||
AUDIT(locked);
|
||||
|
||||
const address_range& range = get_confirmed_range();
|
||||
|
||||
volatile u32* first = get_ptr<volatile u32>(range.start);
|
||||
volatile u32* last = get_ptr<volatile u32>(range.end - 3);
|
||||
|
||||
*first = range.start;
|
||||
*last = range.end;
|
||||
}
|
||||
|
||||
public:
|
||||
bool test_memory_head()
|
||||
{
|
||||
if (guard_policy == protect_policy_full_range)
|
||||
return true;
|
||||
|
||||
AUDIT(locked);
|
||||
|
||||
const auto& range = get_confirmed_range();
|
||||
volatile const u32* first = get_ptr<volatile const u32>(range.start);
|
||||
return (*first == range.start);
|
||||
}
|
||||
|
||||
bool test_memory_tail()
|
||||
{
|
||||
if (guard_policy == protect_policy_full_range)
|
||||
return true;
|
||||
|
||||
AUDIT(locked);
|
||||
|
||||
const auto& range = get_confirmed_range();
|
||||
volatile const u32* last = get_ptr<volatile const u32>(range.end-3);
|
||||
return (*last == range.end);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename pipeline_storage_type, typename backend_storage>
|
||||
class shaders_cache
|
||||
{
|
||||
|
@ -84,6 +84,7 @@
|
||||
<ClCompile Include="Emu\Io\pad_config_types.cpp" />
|
||||
<ClCompile Include="Emu\localized_string.cpp" />
|
||||
<ClCompile Include="Emu\NP\rpcn_config.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_osk_panel.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
|
||||
|
@ -973,9 +973,12 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\interception.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -1899,10 +1902,10 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\dyn_lib.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\RSXDisAsm.h">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClInclude>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||
|
Loading…
Reference in New Issue
Block a user