diff --git a/Utilities/hash.h b/Utilities/hash.h index 2be9f7a7c8..c91cc9a707 100644 --- a/Utilities/hash.h +++ b/Utilities/hash.h @@ -4,23 +4,33 @@ namespace rpcs3 { + constexpr usz fnv_seed = 14695981039346656037ull; + constexpr usz fnv_prime = 1099511628211ull; + template static usz hash_base(T value) { return static_cast(value); } + template::value, bool>> + static inline usz hash64(usz hash_value, const T data) + { + hash_value ^= data; + hash_value *= fnv_prime; + return hash_value; + } + template static usz hash_struct_base(const T& value) { // FNV 64-bit - usz result = 14695981039346656037ull; + usz result = fnv_seed; const U *bits = reinterpret_cast(&value); for (usz n = 0; n < (sizeof(T) / sizeof(U)); ++n) { - result ^= bits[n]; - result *= 1099511628211ull; + result = hash64(result, bits[n]); } return result; diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 52c45680e8..d0833f24eb 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -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 diff --git a/rpcs3/Emu/RSX/Common/texture_cache.cpp b/rpcs3/Emu/RSX/Common/texture_cache.cpp new file mode 100644 index 0000000000..832bc0d596 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/texture_cache.cpp @@ -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& 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(hash_range.start); + auto data64 = reinterpret_cast(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(src)); + src += 4; + } + + if (rem > 2) + { + hash = rpcs3::hash64(hash, *reinterpret_cast(src)); + src += 2; + } + + while (rem--) + { + hash = rpcs3::hash64(hash, *reinterpret_cast(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); + } +} diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 26a2aa793a..19ad88dcaf 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -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 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(tex.get_context()))) { - if constexpr (check_unlocked) - { - if (!tex.is_locked()) - continue; - } - if (required_pitch && !rsx::pitch_compatible(&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]] diff --git a/rpcs3/Emu/RSX/Common/texture_cache_utils.h b/rpcs3/Emu/RSX/Common/texture_cache_utils.h index b1eeea5202..3c14669156 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_utils.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_utils.h @@ -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(range.start), static_cast(range.length()), static_cast(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& 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 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 + inline T* get_ptr(u32 address) const + { + return reinterpret_cast(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 diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index 9ed5c4eb1f..a50aaa8a71 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -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(); diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index 40af0485ea..3c210a2faa 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -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 { diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 2101f7a766..1263e573f8 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -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(range.start), static_cast(range.length()), static_cast(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& 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 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 - inline T* get_ptr(u32 address) const - { - return reinterpret_cast(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(range.start); - volatile u32* last = get_ptr(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(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(range.end-3); - return (*last == range.end); - } - }; - - - template class shaders_cache { diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 85c5189938..328fba027f 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -84,6 +84,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 48dad2c2da..01aa1eb66b 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -973,9 +973,12 @@ Emu\Io - + Emu\GPU\RSX + + + Emu\GPU\RSX\Common @@ -1899,10 +1902,10 @@ Header Files - + Emu\GPU\RSX - +