[Memory] Move new watches to heap-aware Memory from MMIOHandler

This commit is contained in:
Triang3l
2019-07-30 08:00:20 +03:00
parent 83da671bb4
commit 4aceeb73c4
12 changed files with 535 additions and 387 deletions

View File

@@ -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(

View File

@@ -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_;

View 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;

View File

@@ -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.

View File

@@ -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;
}