[Memory] Move new watches to heap-aware Memory from MMIOHandler
This commit is contained in:
@@ -699,11 +699,11 @@ void* PrimitiveConverter::AllocateIndices(
|
||||
return mapping + simd_offset;
|
||||
}
|
||||
|
||||
void PrimitiveConverter::MemoryWriteCallback(uint32_t page_first,
|
||||
uint32_t page_last) {
|
||||
void PrimitiveConverter::MemoryWriteCallback(uint32_t physical_address_start,
|
||||
uint32_t length) {
|
||||
// 1 bit = (512 / 64) MB = 8 MB. Invalidate a region of this size.
|
||||
uint32_t bit_index_first = (page_first * system_page_size_) >> 23;
|
||||
uint32_t bit_index_last = (page_last * system_page_size_) >> 23;
|
||||
uint32_t bit_index_first = physical_address_start >> 23;
|
||||
uint32_t bit_index_last = (physical_address_start + length - 1) >> 23;
|
||||
uint64_t bits = ~((1ull << bit_index_first) - 1);
|
||||
if (bit_index_last < 63) {
|
||||
bits &= (1ull << (bit_index_last + 1)) - 1;
|
||||
@@ -711,11 +711,10 @@ void PrimitiveConverter::MemoryWriteCallback(uint32_t page_first,
|
||||
memory_regions_invalidated_ |= bits;
|
||||
}
|
||||
|
||||
void PrimitiveConverter::MemoryWriteCallbackThunk(void* context_ptr,
|
||||
uint32_t page_first,
|
||||
uint32_t page_last) {
|
||||
void PrimitiveConverter::MemoryWriteCallbackThunk(
|
||||
void* context_ptr, uint32_t physical_address_start, uint32_t length) {
|
||||
reinterpret_cast<PrimitiveConverter*>(context_ptr)
|
||||
->MemoryWriteCallback(page_first, page_last);
|
||||
->MemoryWriteCallback(physical_address_start, length);
|
||||
}
|
||||
|
||||
D3D12_GPU_VIRTUAL_ADDRESS PrimitiveConverter::GetStaticIndexBuffer(
|
||||
|
||||
@@ -89,9 +89,10 @@ class PrimitiveConverter {
|
||||
D3D12_GPU_VIRTUAL_ADDRESS& gpu_address_out);
|
||||
|
||||
// Callback for invalidating buffers mid-frame.
|
||||
void MemoryWriteCallback(uint32_t page_first, uint32_t page_last);
|
||||
static void MemoryWriteCallbackThunk(void* context_ptr, uint32_t page_first,
|
||||
uint32_t page_last);
|
||||
void MemoryWriteCallback(uint32_t physical_address_start, uint32_t length);
|
||||
static void MemoryWriteCallbackThunk(void* context_ptr,
|
||||
uint32_t physical_address_start,
|
||||
uint32_t length);
|
||||
|
||||
D3D12CommandProcessor* command_processor_;
|
||||
RegisterFile* register_file_;
|
||||
|
||||
@@ -172,7 +172,7 @@ SharedMemory::GlobalWatchHandle SharedMemory::RegisterGlobalWatch(
|
||||
watch->callback = callback;
|
||||
watch->callback_context = callback_context;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
global_watches_.push_back(watch);
|
||||
|
||||
return reinterpret_cast<GlobalWatchHandle>(watch);
|
||||
@@ -182,7 +182,7 @@ void SharedMemory::UnregisterGlobalWatch(GlobalWatchHandle handle) {
|
||||
auto watch = reinterpret_cast<GlobalWatch*>(handle);
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
auto it = std::find(global_watches_.begin(), global_watches_.end(), watch);
|
||||
assert_false(it == global_watches_.end());
|
||||
if (it != global_watches_.end()) {
|
||||
@@ -208,7 +208,7 @@ SharedMemory::WatchHandle SharedMemory::WatchMemoryRange(
|
||||
uint32_t bucket_last =
|
||||
watch_page_last << page_size_log2_ >> kWatchBucketSizeLog2;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
// Allocate the range.
|
||||
WatchRange* range = watch_range_first_free_;
|
||||
@@ -267,7 +267,7 @@ void SharedMemory::UnwatchMemoryRange(WatchHandle handle) {
|
||||
// Could be a zero length range.
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
UnlinkWatchRange(reinterpret_cast<WatchRange*>(handle));
|
||||
}
|
||||
|
||||
@@ -405,7 +405,7 @@ void SharedMemory::FireWatches(uint32_t page_first, uint32_t page_last,
|
||||
uint32_t bucket_first = address_first >> kWatchBucketSizeLog2;
|
||||
uint32_t bucket_last = address_last >> kWatchBucketSizeLog2;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
// Fire global watches.
|
||||
for (const auto global_watch : global_watches_) {
|
||||
@@ -472,7 +472,7 @@ void SharedMemory::MakeRangeValid(uint32_t valid_page_first,
|
||||
uint32_t valid_block_last = valid_page_last >> 6;
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
for (uint32_t i = valid_block_first; i <= valid_block_last; ++i) {
|
||||
uint64_t valid_bits = UINT64_MAX;
|
||||
@@ -523,7 +523,7 @@ void SharedMemory::GetRangesToUpload(uint32_t request_page_first,
|
||||
uint32_t request_block_first = request_page_first >> 6;
|
||||
uint32_t request_block_last = request_page_last >> 6;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
uint32_t range_start = UINT32_MAX;
|
||||
for (uint32_t i = request_block_first; i <= request_block_last; ++i) {
|
||||
@@ -570,18 +570,23 @@ void SharedMemory::GetRangesToUpload(uint32_t request_page_first,
|
||||
}
|
||||
|
||||
void SharedMemory::MemoryWriteCallbackThunk(void* context_ptr,
|
||||
uint32_t page_first,
|
||||
uint32_t page_last) {
|
||||
uint32_t physical_address_start,
|
||||
uint32_t length) {
|
||||
reinterpret_cast<SharedMemory*>(context_ptr)
|
||||
->MemoryWriteCallback(page_first, page_last);
|
||||
->MemoryWriteCallback(physical_address_start, length);
|
||||
}
|
||||
|
||||
void SharedMemory::MemoryWriteCallback(uint32_t page_first,
|
||||
uint32_t page_last) {
|
||||
void SharedMemory::MemoryWriteCallback(uint32_t physical_address_start,
|
||||
uint32_t length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
uint32_t page_first = physical_address_start >> page_size_log2_;
|
||||
uint32_t page_last = (physical_address_start + length - 1) >> page_size_log2_;
|
||||
uint32_t block_first = page_first >> 6;
|
||||
uint32_t block_last = page_last >> 6;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(validity_mutex_);
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
for (uint32_t i = block_first; i <= block_last; ++i) {
|
||||
uint64_t invalidate_bits = UINT64_MAX;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/memory.h"
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
#include "xenia/ui/d3d12/pools.h"
|
||||
@@ -73,7 +74,7 @@ class SharedMemory {
|
||||
// additional subsystem/object-specific data (such as whether the range
|
||||
// belongs to the base mip level or to the rest of the mips).
|
||||
//
|
||||
// The callback is called with the mutex locked. Do NOT watch or unwatch
|
||||
// Called with the global critical region locked. Do NOT watch or unwatch
|
||||
// ranges from within it! The watch for the callback is cancelled after the
|
||||
// callback - the handle becomes invalid.
|
||||
WatchHandle WatchMemoryRange(uint32_t start, uint32_t length,
|
||||
@@ -83,8 +84,9 @@ class SharedMemory {
|
||||
void UnwatchMemoryRange(WatchHandle handle);
|
||||
// Locks the mutex that gets locked when watch callbacks are invoked - must be
|
||||
// done when checking variables that may be changed by a watch callback.
|
||||
inline void LockWatchMutex() { validity_mutex_.lock(); }
|
||||
inline void UnlockWatchMutex() { validity_mutex_.unlock(); }
|
||||
inline std::unique_lock<std::recursive_mutex> LockWatchMutex() {
|
||||
return global_critical_region_.Acquire();
|
||||
}
|
||||
|
||||
// Ensures the buffer tiles backing the range are resident, but doesn't upload
|
||||
// anything.
|
||||
@@ -125,8 +127,7 @@ class SharedMemory {
|
||||
private:
|
||||
bool AreTiledResourcesUsed() const;
|
||||
|
||||
// Mark the memory range as updated and protect it. The validity mutex must
|
||||
// NOT be held when calling!!!
|
||||
// Mark the memory range as updated and protect it.
|
||||
void MakeRangeValid(uint32_t valid_page_first, uint32_t valid_page_count);
|
||||
|
||||
D3D12CommandProcessor* command_processor_;
|
||||
@@ -175,19 +176,20 @@ class SharedMemory {
|
||||
|
||||
// Mutex between the exception handler and the command processor, to be locked
|
||||
// when checking or updating validity of pages/ranges.
|
||||
std::recursive_mutex validity_mutex_;
|
||||
xe::global_critical_region global_critical_region_;
|
||||
|
||||
// ***************************************************************************
|
||||
// Things below should be protected by validity_mutex_.
|
||||
// Things below should be protected by global_critical_region.
|
||||
// ***************************************************************************
|
||||
|
||||
// Bit vector containing whether physical memory system pages are up to date.
|
||||
std::vector<uint64_t> valid_pages_;
|
||||
|
||||
// Memory access callback.
|
||||
static void MemoryWriteCallbackThunk(void* context_ptr, uint32_t page_first,
|
||||
uint32_t page_last);
|
||||
void MemoryWriteCallback(uint32_t page_first, uint32_t page_last);
|
||||
static void MemoryWriteCallbackThunk(void* context_ptr,
|
||||
uint32_t physical_address_start,
|
||||
uint32_t length);
|
||||
void MemoryWriteCallback(uint32_t physical_address_start, uint32_t length);
|
||||
|
||||
struct GlobalWatch {
|
||||
GlobalWatchCallback callback;
|
||||
@@ -249,7 +251,7 @@ class SharedMemory {
|
||||
void UnlinkWatchRange(WatchRange* range);
|
||||
|
||||
// ***************************************************************************
|
||||
// Things above should be protected by validity_mutex_.
|
||||
// Things above should be protected by global_critical_region.
|
||||
// ***************************************************************************
|
||||
|
||||
// First page and length in pages.
|
||||
|
||||
@@ -1267,7 +1267,7 @@ void TextureCache::MarkRangeAsResolved(uint32_t start_unscaled,
|
||||
uint32_t page_last = (start_unscaled + length_unscaled - 1) >> 12;
|
||||
uint32_t block_first = page_first >> 5;
|
||||
uint32_t block_last = page_last >> 5;
|
||||
shared_memory_->LockWatchMutex();
|
||||
auto watch_lock = shared_memory_->LockWatchMutex();
|
||||
for (uint32_t i = block_first; i <= block_last; ++i) {
|
||||
uint32_t add_bits = UINT32_MAX;
|
||||
if (i == block_first) {
|
||||
@@ -1279,7 +1279,6 @@ void TextureCache::MarkRangeAsResolved(uint32_t start_unscaled,
|
||||
scaled_resolve_pages_[i] |= add_bits;
|
||||
scaled_resolve_pages_l2_[i >> 6] |= 1ull << (i & 63);
|
||||
}
|
||||
shared_memory_->UnlockWatchMutex();
|
||||
}
|
||||
|
||||
// Invalidate textures. Toggling individual textures between scaled and
|
||||
@@ -1970,10 +1969,12 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
|
||||
|
||||
bool TextureCache::LoadTextureData(Texture* texture) {
|
||||
// See what we need to upload.
|
||||
shared_memory_->LockWatchMutex();
|
||||
bool base_in_sync = texture->base_in_sync;
|
||||
bool mips_in_sync = texture->mips_in_sync;
|
||||
shared_memory_->UnlockWatchMutex();
|
||||
bool base_in_sync, mips_in_sync;
|
||||
{
|
||||
auto watch_lock = shared_memory_->LockWatchMutex();
|
||||
base_in_sync = texture->base_in_sync;
|
||||
mips_in_sync = texture->mips_in_sync;
|
||||
}
|
||||
if (base_in_sync && mips_in_sync) {
|
||||
return true;
|
||||
}
|
||||
@@ -2235,20 +2236,21 @@ bool TextureCache::LoadTextureData(Texture* texture) {
|
||||
// resolves as well to detect when the CPU wants to reuse the memory for a
|
||||
// regular texture or a vertex buffer, and thus the scaled resolve version is
|
||||
// not up to date anymore.
|
||||
shared_memory_->LockWatchMutex();
|
||||
texture->base_in_sync = true;
|
||||
texture->mips_in_sync = true;
|
||||
if (!base_in_sync) {
|
||||
texture->base_watch_handle = shared_memory_->WatchMemoryRange(
|
||||
texture->key.base_page << 12, texture->base_size, WatchCallbackThunk,
|
||||
this, texture, 0);
|
||||
{
|
||||
auto watch_lock = shared_memory_->LockWatchMutex();
|
||||
texture->base_in_sync = true;
|
||||
texture->mips_in_sync = true;
|
||||
if (!base_in_sync) {
|
||||
texture->base_watch_handle = shared_memory_->WatchMemoryRange(
|
||||
texture->key.base_page << 12, texture->base_size, WatchCallbackThunk,
|
||||
this, texture, 0);
|
||||
}
|
||||
if (!mips_in_sync) {
|
||||
texture->mip_watch_handle = shared_memory_->WatchMemoryRange(
|
||||
texture->key.mip_page << 12, texture->mip_size, WatchCallbackThunk,
|
||||
this, texture, 1);
|
||||
}
|
||||
}
|
||||
if (!mips_in_sync) {
|
||||
texture->mip_watch_handle = shared_memory_->WatchMemoryRange(
|
||||
texture->key.mip_page << 12, texture->mip_size, WatchCallbackThunk,
|
||||
this, texture, 1);
|
||||
}
|
||||
shared_memory_->UnlockWatchMutex();
|
||||
|
||||
LogTextureAction(texture, "Loaded");
|
||||
return true;
|
||||
@@ -2325,7 +2327,7 @@ bool TextureCache::IsRangeScaledResolved(uint32_t start_unscaled,
|
||||
uint32_t block_last = page_last >> 5;
|
||||
uint32_t l2_block_first = block_first >> 6;
|
||||
uint32_t l2_block_last = block_last >> 6;
|
||||
shared_memory_->LockWatchMutex();
|
||||
auto watch_lock = shared_memory_->LockWatchMutex();
|
||||
for (uint32_t i = l2_block_first; i <= l2_block_last; ++i) {
|
||||
uint64_t l2_block = scaled_resolve_pages_l2_[i];
|
||||
if (i == l2_block_first) {
|
||||
@@ -2346,12 +2348,10 @@ bool TextureCache::IsRangeScaledResolved(uint32_t start_unscaled,
|
||||
check_bits &= (1u << ((page_last & 31) + 1)) - 1;
|
||||
}
|
||||
if (scaled_resolve_pages_[block_index] & check_bits) {
|
||||
shared_memory_->UnlockWatchMutex();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
shared_memory_->UnlockWatchMutex();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user