[GPU] Avoid stale texture state after invalidation

Check shared memory validity before marking textures up to date and installing watches. If the backing range was invalidated, leave the texture outdated so it can be reloaded later.
This commit is contained in:
Michael Oliver
2026-06-29 08:41:47 +01:00
committed by Radosław Gliński
parent a421f8da52
commit 9781a75a22
4 changed files with 57 additions and 6 deletions

View File

@@ -416,6 +416,34 @@ bool SharedMemory::RequestRange(uint32_t start, uint32_t length) {
return UploadRanges(uploads, current_upload_range);
}
bool SharedMemory::IsRangeValid(uint32_t start, uint32_t length) const {
if (!length) {
return true;
}
if (start > kBufferSize || (kBufferSize - start) < length) {
return false;
}
const uint32_t page_first = start >> page_size_log2_;
const uint32_t page_last = (start + length - 1) >> page_size_log2_;
const uint32_t block_first = page_first >> 6;
const uint32_t block_last = page_last >> 6;
for (uint32_t i = block_first; i <= block_last; ++i) {
uint64_t valid_mask = UINT64_MAX;
if (i == block_first) {
valid_mask &= ~((uint64_t(1) << (page_first & 63)) - 1);
}
if (i == block_last && (page_last & 63) != 63) {
valid_mask &= (uint64_t(1) << ((page_last & 63) + 1)) - 1;
}
if ((system_page_flags_valid_[i] & valid_mask) != valid_mask) {
return false;
}
}
return true;
}
template <typename T>
XE_FORCEINLINE XE_NOALIAS static T mod_shift_left(T value, uint32_t by) {
#if XE_ARCH_AMD64 == 1

View File

@@ -72,6 +72,10 @@ class SharedMemory {
// ensures the host GPU memory backing the range are resident. Returns true if
// the range has been fully updated and is usable.
bool RequestRange(uint32_t start, uint32_t length);
// Returns whether every page in the range is currently valid in the host GPU
// memory copy. Hold the global critical region if relying on this for state
// transitions such as watch installation.
bool IsRangeValid(uint32_t start, uint32_t length) const;
void TryFindUploadRange(const uint32_t& block_first,
const uint32_t& block_last,

View File

@@ -517,23 +517,38 @@ TextureCache::Texture::~Texture() {
texture_cache_.UpdateTexturesTotalHostMemoryUsage(0, host_memory_usage_);
}
void TextureCache::Texture::MakeUpToDateAndWatch(
bool TextureCache::Texture::MakeUpToDateAndWatch(
const global_unique_lock_type& global_lock) {
SharedMemory& shared_memory = texture_cache().shared_memory();
if (base_outdated_) {
const bool watch_base = base_outdated_;
const bool watch_mips = mips_outdated_;
assert_true(global_lock.owns_lock());
if (watch_base &&
!shared_memory.IsRangeValid(
key().base_page << 12, xe::align(GetGuestBaseSize(), UINT32_C(16)))) {
return false;
}
if (watch_mips &&
!shared_memory.IsRangeValid(
key().mip_page << 12, xe::align(GetGuestMipsSize(), UINT32_C(16)))) {
return false;
}
if (watch_base) {
assert_not_zero(GetGuestBaseSize());
base_outdated_ = false;
base_watch_handle_ = shared_memory.WatchMemoryRange(
key().base_page << 12, GetGuestBaseSize(), TextureCache::WatchCallback,
this, nullptr, 0);
}
if (mips_outdated_) {
if (watch_mips) {
assert_not_zero(GetGuestMipsSize());
mips_outdated_ = false;
mips_watch_handle_ = shared_memory.WatchMemoryRange(
key().mip_page << 12, GetGuestMipsSize(), TextureCache::WatchCallback,
this, nullptr, 1);
}
return true;
}
void TextureCache::Texture::MarkAsUsed() {
@@ -781,7 +796,9 @@ void TextureCache::LoadTexturesData(Texture** textures, uint32_t n_textures) {
// 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.
texture->MakeUpToDateAndWatch(crit);
if (!texture->MakeUpToDateAndWatch(crit)) {
continue;
}
texture->LogAction("Loaded");
}
@@ -861,7 +878,9 @@ 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.
texture.MakeUpToDateAndWatch(global_critical_region_.Acquire());
if (!texture.MakeUpToDateAndWatch(global_critical_region_.Acquire())) {
return false;
}
texture.LogAction("Loaded");

View File

@@ -274,7 +274,7 @@ class TextureCache {
// not).
bool base_outdated_lockless() const { return base_outdated_; }
bool mips_outdated_lockless() const { return mips_outdated_; }
void MakeUpToDateAndWatch(const global_unique_lock_type& global_lock);
bool MakeUpToDateAndWatch(const global_unique_lock_type& global_lock);
void WatchCallback(const global_unique_lock_type& global_lock, bool is_mip);