mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-29 22:20:48 +00: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
|
namespace rpcs3
|
||||||
{
|
{
|
||||||
|
constexpr usz fnv_seed = 14695981039346656037ull;
|
||||||
|
constexpr usz fnv_prime = 1099511628211ull;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static usz hash_base(T value)
|
static usz hash_base(T value)
|
||||||
{
|
{
|
||||||
return static_cast<usz>(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>
|
template<typename T, typename U>
|
||||||
static usz hash_struct_base(const T& value)
|
static usz hash_struct_base(const T& value)
|
||||||
{
|
{
|
||||||
// FNV 64-bit
|
// FNV 64-bit
|
||||||
usz result = 14695981039346656037ull;
|
usz result = fnv_seed;
|
||||||
const U *bits = reinterpret_cast<const U*>(&value);
|
const U *bits = reinterpret_cast<const U*>(&value);
|
||||||
|
|
||||||
for (usz n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
for (usz n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||||
{
|
{
|
||||||
result ^= bits[n];
|
result = hash64(result, bits[n]);
|
||||||
result *= 1099511628211ull;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -406,6 +406,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||||||
RSX/Common/ProgramStateCache.cpp
|
RSX/Common/ProgramStateCache.cpp
|
||||||
RSX/Common/surface_store.cpp
|
RSX/Common/surface_store.cpp
|
||||||
RSX/Common/TextureUtils.cpp
|
RSX/Common/TextureUtils.cpp
|
||||||
|
RSX/Common/texture_cache.cpp
|
||||||
RSX/Common/VertexProgramDecompiler.cpp
|
RSX/Common/VertexProgramDecompiler.cpp
|
||||||
RSX/Null/NullGSRender.cpp
|
RSX/Null/NullGSRender.cpp
|
||||||
RSX/Overlays/overlay_animation.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;
|
u32 last_dirty_block = UINT32_MAX;
|
||||||
bool repeat_loop = false;
|
bool repeat_loop = false;
|
||||||
|
|
||||||
// Not having full-range protections means some textures will check the confirmed range and not the locked range
|
auto It = m_storage.range_begin(invalidate_range, locked_range, true); // will iterate through locked sections only
|
||||||
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
|
|
||||||
while (It != m_storage.range_end())
|
while (It != m_storage.range_end())
|
||||||
{
|
{
|
||||||
const u32 base = It.get_block().get_start();
|
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
|
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();
|
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();
|
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));
|
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
|
// 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())
|
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;
|
repeat_loop = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -863,7 +866,7 @@ namespace rsx
|
|||||||
// Write if and only if no one else has trashed section memory already
|
// Write if and only if no one else has trashed section memory already
|
||||||
// TODO: Proper section management should prevent this from happening
|
// TODO: Proper section management should prevent this from happening
|
||||||
// TODO: Blit engine section merge support and/or partial texture memory buffering
|
// 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
|
// Contents clobbered, destroy this
|
||||||
if (!tex.is_dirty())
|
if (!tex.is_dirty())
|
||||||
@ -973,18 +976,17 @@ namespace rsx
|
|||||||
{
|
{
|
||||||
std::vector<section_storage_type*> results;
|
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;
|
auto &tex = *It;
|
||||||
|
|
||||||
|
if constexpr (check_unlocked)
|
||||||
|
{
|
||||||
|
tex.sync_protection();
|
||||||
|
}
|
||||||
|
|
||||||
if (!tex.is_dirty() && (context_mask & static_cast<u32>(tex.get_context())))
|
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))
|
if (required_pitch && !rsx::pitch_compatible<false>(&tex, required_pitch, UINT16_MAX))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -1393,6 +1395,13 @@ namespace rsx
|
|||||||
return false;
|
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)
|
image_view_type create_temporary_subresource(commandbuffer_type &cmd, deferred_subresource& desc)
|
||||||
{
|
{
|
||||||
if (!desc.do_not_cache) [[likely]]
|
if (!desc.do_not_cache) [[likely]]
|
||||||
|
@ -10,6 +10,31 @@
|
|||||||
|
|
||||||
namespace rsx
|
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 structure used in Ranged Storage Blocks
|
||||||
* List of Arrays
|
* List of Arrays
|
||||||
@ -603,6 +628,24 @@ namespace rsx
|
|||||||
return any_released;
|
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
|
* 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
|
* Cached Texture Section
|
||||||
@ -1287,6 +1480,15 @@ namespace rsx
|
|||||||
flush_exclusions.clear();
|
flush_exclusions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sync_protection()
|
||||||
|
{
|
||||||
|
if (!buffered_section::sync())
|
||||||
|
{
|
||||||
|
discard(true);
|
||||||
|
ensure(is_dirty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush
|
* Flush
|
||||||
@ -1522,13 +1724,7 @@ namespace rsx
|
|||||||
|
|
||||||
rsx::section_bounds get_overlap_test_bounds() const
|
rsx::section_bounds get_overlap_test_bounds() const
|
||||||
{
|
{
|
||||||
if (guard_policy == protection_policy::protect_policy_full_range)
|
return rsx::section_bounds::locked_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rsx::texture_dimension_extended get_image_type() const
|
rsx::texture_dimension_extended get_image_type() const
|
||||||
|
@ -873,6 +873,8 @@ namespace gl
|
|||||||
|
|
||||||
void on_frame_end() override
|
void on_frame_end() override
|
||||||
{
|
{
|
||||||
|
trim_sections();
|
||||||
|
|
||||||
if (m_storage.m_unreleased_texture_objects >= m_max_zombie_objects)
|
if (m_storage.m_unreleased_texture_objects >= m_max_zombie_objects)
|
||||||
{
|
{
|
||||||
purge_unreleased_sections();
|
purge_unreleased_sections();
|
||||||
|
@ -1098,6 +1098,8 @@ namespace vk
|
|||||||
|
|
||||||
void on_frame_end() override
|
void on_frame_end() override
|
||||||
{
|
{
|
||||||
|
trim_sections();
|
||||||
|
|
||||||
if (m_storage.m_unreleased_texture_objects >= m_max_zombie_objects ||
|
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
|
m_temporary_memory_size > 0x4000000) //If already holding over 64M in discardable memory, be frugal with memory resources
|
||||||
{
|
{
|
||||||
|
@ -20,373 +20,6 @@
|
|||||||
|
|
||||||
namespace rsx
|
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>
|
template <typename pipeline_storage_type, typename backend_storage>
|
||||||
class shaders_cache
|
class shaders_cache
|
||||||
{
|
{
|
||||||
|
@ -84,6 +84,7 @@
|
|||||||
<ClCompile Include="Emu\Io\pad_config_types.cpp" />
|
<ClCompile Include="Emu\Io\pad_config_types.cpp" />
|
||||||
<ClCompile Include="Emu\localized_string.cpp" />
|
<ClCompile Include="Emu\localized_string.cpp" />
|
||||||
<ClCompile Include="Emu\NP\rpcn_config.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_osk_panel.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
|
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
|
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
|
||||||
|
@ -973,9 +973,12 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Emu\Io\interception.cpp">
|
<ClCompile Include="Emu\Io\interception.cpp">
|
||||||
<Filter>Emu\Io</Filter>
|
<Filter>Emu\Io</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp">
|
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp">
|
||||||
<Filter>Emu\GPU\RSX</Filter>
|
<Filter>Emu\GPU\RSX</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp">
|
||||||
|
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -1899,10 +1902,10 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\Utilities\dyn_lib.hpp">
|
<ClInclude Include="..\Utilities\dyn_lib.hpp">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="Emu\RSX\RSXDisAsm.h">
|
<ClInclude Include="Emu\RSX\RSXDisAsm.h">
|
||||||
<Filter>Emu\GPU\RSX</Filter>
|
<Filter>Emu\GPU\RSX</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user