mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
renderer: remove legacy code left from refactoring
This commit is contained in:
@@ -157,7 +157,6 @@ void MemoryTracker::UntrackMemoryLocked(uint64_t vaddr, uint64_t size) {
|
|||||||
const auto changed =
|
const auto changed =
|
||||||
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||||
manager->ApplyProtection(changed, false);
|
manager->ApplyProtection(changed, false);
|
||||||
manager->Untrack(manager->GetCpuAddr() + offset, bytes);
|
|
||||||
});
|
});
|
||||||
locks.clear();
|
locks.clear();
|
||||||
}
|
}
|
||||||
@@ -168,93 +167,4 @@ void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t size) {
|
|||||||
UntrackMemoryLocked(vaddr, size);
|
UntrackMemoryLocked(vaddr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemoryTracker::InvalidateRegion(uint64_t vaddr, uint64_t size, PageFaultPhase phase) noexcept {
|
|
||||||
switch (phase) {
|
|
||||||
case PageFaultPhase::Release: return true;
|
|
||||||
case PageFaultPhase::Invalidate: {
|
|
||||||
const auto action = BeginCpuFault(vaddr, size);
|
|
||||||
switch (action) {
|
|
||||||
case CpuFaultAction::Untracked: return false;
|
|
||||||
case CpuFaultAction::Continue: return true;
|
|
||||||
case CpuFaultAction::Download:
|
|
||||||
EXIT("generic region invalidation cannot download GPU-dirty memory\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case PageFaultPhase::Complete:
|
|
||||||
return CompleteCpuFault(vaddr, size, PageFaultAccess::Write, false);
|
|
||||||
}
|
|
||||||
EXIT("unsupported region invalidation phase\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MemoryTracker::InvalidateVirtualGpuWrite(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept {
|
|
||||||
switch (phase) {
|
|
||||||
case PageFaultPhase::Release: return true;
|
|
||||||
case PageFaultPhase::Invalidate: {
|
|
||||||
const bool gpu_modified = Iterate<false>(
|
|
||||||
vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
|
||||||
std::scoped_lock lock(manager->lock);
|
|
||||||
return manager->IsModified<DirtySource::Gpu>(offset, bytes);
|
|
||||||
});
|
|
||||||
if (!gpu_modified) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto action = BeginCpuFault(vaddr, size);
|
|
||||||
if (access != PageFaultAccess::Write || action != CpuFaultAction::Download) {
|
|
||||||
EXIT("virtual GPU write fault requires write access to GPU-dirty memory\n");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case PageFaultPhase::Complete: {
|
|
||||||
if (access != PageFaultAccess::Write) {
|
|
||||||
EXIT("virtual GPU write completion requires write access\n");
|
|
||||||
}
|
|
||||||
bool completed = false;
|
|
||||||
Iterate<false>(
|
|
||||||
vaddr, size, [&completed](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
|
||||||
std::scoped_lock lock(manager->lock);
|
|
||||||
if (completed) {
|
|
||||||
EXIT("virtual GPU write fault spans multiple tracked regions\n");
|
|
||||||
}
|
|
||||||
completed =
|
|
||||||
manager->CompleteVirtualGpuWrite(manager->GetCpuAddr() + offset, bytes);
|
|
||||||
});
|
|
||||||
return completed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXIT("unsupported virtual GPU write invalidation phase\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
CpuFaultAction MemoryTracker::BeginCpuFault(uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultAccess access) noexcept {
|
|
||||||
CheckNotInUploadCallback();
|
|
||||||
CpuFaultAction action = CpuFaultAction::Untracked;
|
|
||||||
Iterate<false>(
|
|
||||||
vaddr, size, [&action, access](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
|
||||||
std::scoped_lock lock(manager->lock);
|
|
||||||
if (action != CpuFaultAction::Untracked) {
|
|
||||||
EXIT("CPU fault spans multiple tracked regions\n");
|
|
||||||
}
|
|
||||||
action = manager->BeginCpuFault(manager->GetCpuAddr() + offset, bytes, access);
|
|
||||||
});
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MemoryTracker::CompleteCpuFault(uint64_t vaddr, uint64_t size, PageFaultAccess access,
|
|
||||||
bool downloaded) noexcept {
|
|
||||||
CheckNotInUploadCallback();
|
|
||||||
bool found = false;
|
|
||||||
Iterate<false>(
|
|
||||||
vaddr, size,
|
|
||||||
[&found, access, downloaded](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
|
||||||
std::scoped_lock lock(manager->lock);
|
|
||||||
if (found) {
|
|
||||||
EXIT("CPU fault completion spans multiple tracked regions\n");
|
|
||||||
}
|
|
||||||
found = manager->CompleteCpuFault(manager->GetCpuAddr() + offset, bytes, access,
|
|
||||||
downloaded);
|
|
||||||
});
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
@@ -30,13 +30,6 @@ public:
|
|||||||
void MarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
void MarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
||||||
void UnmarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
void UnmarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
||||||
void UntrackMemory(uint64_t vaddr, uint64_t size);
|
void UntrackMemory(uint64_t vaddr, uint64_t size);
|
||||||
[[nodiscard]] CpuFaultAction
|
|
||||||
BeginCpuFault(uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultAccess access = PageFaultAccess::Write) noexcept;
|
|
||||||
[[nodiscard]] bool CompleteCpuFault(uint64_t vaddr, uint64_t size, PageFaultAccess access,
|
|
||||||
bool downloaded) noexcept;
|
|
||||||
[[nodiscard]] bool InvalidateRegion(uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept;
|
|
||||||
template <typename Flush>
|
template <typename Flush>
|
||||||
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) {
|
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) {
|
||||||
static_assert(std::is_invocable_v<Flush&>);
|
static_assert(std::is_invocable_v<Flush&>);
|
||||||
@@ -78,10 +71,8 @@ public:
|
|||||||
EXIT("memory invalidation retained GPU-owned pages\n");
|
EXIT("memory invalidation retained GPU-owned pages\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[[nodiscard]] bool InvalidateVirtualGpuWrite(PageFaultAccess access, uint64_t vaddr,
|
void ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||||
uint64_t size, PageFaultPhase phase) noexcept;
|
const char* operation) const noexcept;
|
||||||
void ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
|
||||||
const char* operation) const noexcept;
|
|
||||||
void ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
void ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||||
const char* operation);
|
const char* operation);
|
||||||
|
|
||||||
@@ -102,9 +93,6 @@ public:
|
|||||||
}
|
}
|
||||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||||
const auto address = manager->GetCpuAddr() + offset;
|
const auto address = manager->GetCpuAddr() + offset;
|
||||||
if (manager->HasPendingFault(address, bytes)) {
|
|
||||||
EXIT("GPU download synchronization raced a pending CPU fault\n");
|
|
||||||
}
|
|
||||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(address, bytes,
|
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(address, bytes,
|
||||||
preflight);
|
preflight);
|
||||||
});
|
});
|
||||||
@@ -141,7 +129,6 @@ public:
|
|||||||
const auto* previous_upload_owner = std::exchange(s_upload_owner, this);
|
const auto* previous_upload_owner = std::exchange(s_upload_owner, this);
|
||||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||||
manager->lock.lock();
|
manager->lock.lock();
|
||||||
manager->Track(manager->GetCpuAddr() + offset, bytes);
|
|
||||||
manager->ForEachModifiedRange<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset,
|
manager->ForEachModifiedRange<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset,
|
||||||
bytes, range_func);
|
bytes, range_func);
|
||||||
if (!is_written) {
|
if (!is_written) {
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
|||||||
// Zero is the unknown protection sentinel.
|
// Zero is the unknown protection sentinel.
|
||||||
constexpr uint32_t UNKNOWN_PROTECTION = 0;
|
constexpr uint32_t UNKNOWN_PROTECTION = 0;
|
||||||
|
|
||||||
thread_local bool g_in_fault_resolution = false;
|
|
||||||
|
|
||||||
[[noreturn]] void FailFast(const char* reason = nullptr) noexcept {
|
[[noreturn]] void FailFast(const char* reason = nullptr) noexcept {
|
||||||
std::fputs("PageManager fail-fast: ", stderr);
|
std::fputs("PageManager fail-fast: ", stderr);
|
||||||
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
||||||
@@ -162,11 +160,8 @@ struct PageManager::Impl {
|
|||||||
uint32_t original_protection = 0;
|
uint32_t original_protection = 0;
|
||||||
uint32_t backing_writer = 0;
|
uint32_t backing_writer = 0;
|
||||||
// Shadow the protection applied through Protect().
|
// Shadow the protection applied through Protect().
|
||||||
uint32_t current_protection = UNKNOWN_PROTECTION;
|
uint32_t current_protection = UNKNOWN_PROTECTION;
|
||||||
bool resolving = false;
|
bool resolving = false;
|
||||||
bool resolving_read_write = false;
|
|
||||||
bool late_read_pending = false;
|
|
||||||
bool late_write_pending = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Region {
|
struct Region {
|
||||||
@@ -193,10 +188,7 @@ struct PageManager::Impl {
|
|||||||
std::span<PageState*> m_pages;
|
std::span<PageState*> m_pages;
|
||||||
};
|
};
|
||||||
|
|
||||||
Impl(PageFaultHandler handler, void* context): fault_handler(handler), fault_context(context) {
|
Impl() {
|
||||||
if (fault_handler == nullptr) {
|
|
||||||
Fatal("null page-manager fault callback");
|
|
||||||
}
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
SYSTEM_INFO info {};
|
SYSTEM_INFO info {};
|
||||||
GetSystemInfo(&info);
|
GetSystemInfo(&info);
|
||||||
@@ -268,17 +260,6 @@ struct PageManager::Impl {
|
|||||||
return page.original_protection;
|
return page.original_protection;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PublishDelayedFaults(PageState& page, uint32_t old_protection,
|
|
||||||
uint32_t new_protection) {
|
|
||||||
if (old_protection == NO_ACCESS_PROTECTION && new_protection != NO_ACCESS_PROTECTION) {
|
|
||||||
page.late_read_pending = true;
|
|
||||||
}
|
|
||||||
if ((old_protection == NO_ACCESS_PROTECTION || old_protection == READ_ONLY_PROTECTION) &&
|
|
||||||
new_protection == READ_WRITE_PROTECTION) {
|
|
||||||
page.late_write_pending = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void InitializeProtection(std::span<PageState*> pages) {
|
static void InitializeProtection(std::span<PageState*> pages) {
|
||||||
for (auto* page: pages) {
|
for (auto* page: pages) {
|
||||||
page->original_protection = READ_WRITE_PROTECTION;
|
page->original_protection = READ_WRITE_PROTECTION;
|
||||||
@@ -286,19 +267,8 @@ struct PageManager::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AllowsAccess(const PageState& page, [[maybe_unused]] uint64_t vaddr,
|
|
||||||
PageFaultAccess access) noexcept {
|
|
||||||
switch (access) {
|
|
||||||
case PageFaultAccess::Read:
|
|
||||||
return page.current_protection == READ_ONLY_PROTECTION ||
|
|
||||||
page.current_protection == READ_WRITE_PROTECTION;
|
|
||||||
case PageFaultAccess::Write: return page.current_protection == READ_WRITE_PROTECTION;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProtectRange(std::span<PageState*> pages, uint64_t vaddr, uint32_t protection,
|
void ProtectRange(std::span<PageState*> pages, uint64_t vaddr, uint32_t protection,
|
||||||
std::span<const uint32_t> expected_old, bool fault_path) noexcept {
|
std::span<const uint32_t> expected_old) noexcept {
|
||||||
const auto size = pages.size() * PAGE_SIZE;
|
const auto size = pages.size() * PAGE_SIZE;
|
||||||
if (pages.size() != expected_old.size()) {
|
if (pages.size() != expected_old.size()) {
|
||||||
FailFast("protection range state size mismatch");
|
FailFast("protection range state size mismatch");
|
||||||
@@ -306,9 +276,6 @@ struct PageManager::Impl {
|
|||||||
for (size_t i = 0; i < pages.size(); i++) {
|
for (size_t i = 0; i < pages.size(); i++) {
|
||||||
const auto actual = pages[i]->current_protection;
|
const auto actual = pages[i]->current_protection;
|
||||||
if (actual != UNKNOWN_PROTECTION && actual != expected_old[i]) {
|
if (actual != UNKNOWN_PROTECTION && actual != expected_old[i]) {
|
||||||
if (fault_path) {
|
|
||||||
FailFast("mprotect fault transition did not match expected protection");
|
|
||||||
}
|
|
||||||
Fatal("invalid protection transition at 0x%016" PRIx64 ", old=0x%08" PRIx32
|
Fatal("invalid protection transition at 0x%016" PRIx64 ", old=0x%08" PRIx32
|
||||||
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
|
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
|
||||||
vaddr + i * PAGE_SIZE, actual, expected_old[i], protection);
|
vaddr + i * PAGE_SIZE, actual, expected_old[i], protection);
|
||||||
@@ -316,9 +283,6 @@ struct PageManager::Impl {
|
|||||||
}
|
}
|
||||||
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size,
|
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size,
|
||||||
ToMemoryMode(protection))) {
|
ToMemoryMode(protection))) {
|
||||||
if (fault_path) {
|
|
||||||
FailFast("address-space fault protection transition failed");
|
|
||||||
}
|
|
||||||
Fatal("address-space protection failed at 0x%016" PRIx64 ", new=0x%08" PRIx32, vaddr,
|
Fatal("address-space protection failed at 0x%016" PRIx64 ", new=0x%08" PRIx32, vaddr,
|
||||||
protection);
|
protection);
|
||||||
}
|
}
|
||||||
@@ -327,47 +291,28 @@ struct PageManager::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Protect(PageState& page, uint64_t vaddr, uint32_t protection, uint32_t expected_old,
|
void Protect(PageState& page, uint64_t vaddr, uint32_t protection,
|
||||||
bool fault_path) noexcept {
|
uint32_t expected_old) noexcept {
|
||||||
PageState* pages[] = {&page};
|
PageState* pages[] = {&page};
|
||||||
uint32_t expected[] = {expected_old};
|
uint32_t expected[] = {expected_old};
|
||||||
ProtectRange(pages, vaddr, protection, expected, fault_path);
|
ProtectRange(pages, vaddr, protection, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<std::atomic<Region*>[]> regions;
|
std::unique_ptr<std::atomic<Region*>[]> regions;
|
||||||
std::vector<std::unique_ptr<Region>> region_storage;
|
std::vector<std::unique_ptr<Region>> region_storage;
|
||||||
std::mutex region_mutex;
|
std::mutex region_mutex;
|
||||||
PageFaultHandler fault_handler = nullptr;
|
|
||||||
void* fault_context = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::atomic<void*>::is_always_lock_free);
|
static_assert(std::atomic<void*>::is_always_lock_free);
|
||||||
|
|
||||||
PageManager::PageManager(PageFaultHandler fault_handler, void* fault_context)
|
PageManager::PageManager(): m_impl(std::make_unique<Impl>()) {}
|
||||||
: m_impl(std::make_unique<Impl>(fault_handler, fault_context)) {}
|
|
||||||
|
|
||||||
PageManager::~PageManager() = default;
|
PageManager::~PageManager() = default;
|
||||||
|
|
||||||
uint64_t PageManager::GetPageSize() const {
|
uint64_t PageManager::GetPageSize() const {
|
||||||
if (g_in_fault_resolution) {
|
|
||||||
FailFast("nested page fault while resolving a watched page");
|
|
||||||
}
|
|
||||||
return PAGE_SIZE;
|
return PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageManager::IsTracked(uint64_t vaddr) const noexcept {
|
|
||||||
if (g_in_fault_resolution) {
|
|
||||||
FailFast("IsTracked called during fault resolution");
|
|
||||||
}
|
|
||||||
auto* region = m_impl->FindRegion(vaddr);
|
|
||||||
if (region == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto& page = m_impl->GetPage(*region, vaddr);
|
|
||||||
SpinGuard lock(page.lock);
|
|
||||||
return page.write_watchers != 0 || page.access_watchers != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageManager::UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
|
void PageManager::UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
|
||||||
PageWatchMode mode) {
|
PageWatchMode mode) {
|
||||||
if (mode != PageWatchMode::Write && mode != PageWatchMode::ReadWrite) {
|
if (mode != PageWatchMode::Write && mode != PageWatchMode::ReadWrite) {
|
||||||
@@ -473,27 +418,14 @@ void PageManager::UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
|
|||||||
}
|
}
|
||||||
m_impl->ProtectRange(std::span {pages}.subspan(first, last - first),
|
m_impl->ProtectRange(std::span {pages}.subspan(first, last - first),
|
||||||
chunk_begin + first * PAGE_SIZE, protection,
|
chunk_begin + first * PAGE_SIZE, protection,
|
||||||
std::span {old_protections}.subspan(first, last - first), false);
|
std::span {old_protections}.subspan(first, last - first));
|
||||||
first = current;
|
first = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < page_count; i++) {
|
for (auto* page: pages) {
|
||||||
auto& page = *pages[i];
|
if (!track && page->backing_writer == 0 && page->write_watchers == 0 &&
|
||||||
const auto protection = new_protections[i];
|
page->access_watchers == 0) {
|
||||||
if (track) {
|
page->original_protection = 0;
|
||||||
switch (protection) {
|
|
||||||
case NO_ACCESS_PROTECTION:
|
|
||||||
page.late_read_pending = false;
|
|
||||||
page.late_write_pending = false;
|
|
||||||
break;
|
|
||||||
case READ_ONLY_PROTECTION: page.late_write_pending = false; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
} else if (page.backing_writer == 0) {
|
|
||||||
Impl::PublishDelayedFaults(page, old_protections[i], protection);
|
|
||||||
if (page.write_watchers == 0 && page.access_watchers == 0) {
|
|
||||||
page.original_protection = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chunk_begin = chunk_end;
|
chunk_begin = chunk_end;
|
||||||
@@ -546,9 +478,6 @@ PageManager::ReserveBackingWrites(std::span<const RangeSet::Range> ranges) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PageManager::BeginBackingWrite(uint64_t vaddr, uint64_t size) noexcept {
|
void PageManager::BeginBackingWrite(uint64_t vaddr, uint64_t size) noexcept {
|
||||||
if (g_in_fault_resolution) {
|
|
||||||
FailFast("backing write began during fault resolution");
|
|
||||||
}
|
|
||||||
const auto end = PageEnd(vaddr, size);
|
const auto end = PageEnd(vaddr, size);
|
||||||
const auto writer = CurrentThread();
|
const auto writer = CurrentThread();
|
||||||
for (auto address = PageStart(vaddr); address < end; address += PAGE_SIZE) {
|
for (auto address = PageStart(vaddr); address < end; address += PAGE_SIZE) {
|
||||||
@@ -561,16 +490,12 @@ void PageManager::BeginBackingWrite(uint64_t vaddr, uint64_t size) noexcept {
|
|||||||
if (page.resolving || page.backing_writer != 0 || page.access_watchers == 0) {
|
if (page.resolving || page.backing_writer != 0 || page.access_watchers == 0) {
|
||||||
Fatal("backing write races page resolution at 0x%016" PRIx64, address);
|
Fatal("backing write races page resolution at 0x%016" PRIx64, address);
|
||||||
}
|
}
|
||||||
page.resolving = true;
|
page.resolving = true;
|
||||||
page.resolving_read_write = true;
|
page.backing_writer = writer;
|
||||||
page.backing_writer = writer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageManager::EndBackingWrite(uint64_t vaddr, uint64_t size) noexcept {
|
void PageManager::EndBackingWrite(uint64_t vaddr, uint64_t size) noexcept {
|
||||||
if (g_in_fault_resolution) {
|
|
||||||
FailFast("backing write ended during fault resolution");
|
|
||||||
}
|
|
||||||
const auto end = PageEnd(vaddr, size);
|
const auto end = PageEnd(vaddr, size);
|
||||||
const auto writer = CurrentThread();
|
const auto writer = CurrentThread();
|
||||||
for (auto address = PageStart(vaddr); address < end; address += PAGE_SIZE) {
|
for (auto address = PageStart(vaddr); address < end; address += PAGE_SIZE) {
|
||||||
@@ -586,127 +511,14 @@ void PageManager::EndBackingWrite(uint64_t vaddr, uint64_t size) noexcept {
|
|||||||
const auto old_protection = NO_ACCESS_PROTECTION;
|
const auto old_protection = NO_ACCESS_PROTECTION;
|
||||||
const auto new_protection = Impl::WatcherProtection(page);
|
const auto new_protection = Impl::WatcherProtection(page);
|
||||||
if (new_protection != old_protection) {
|
if (new_protection != old_protection) {
|
||||||
m_impl->Protect(page, address, new_protection, old_protection, false);
|
m_impl->Protect(page, address, new_protection, old_protection);
|
||||||
}
|
}
|
||||||
Impl::PublishDelayedFaults(page, old_protection, new_protection);
|
|
||||||
if (page.write_watchers == 0 && page.access_watchers == 0) {
|
if (page.write_watchers == 0 && page.access_watchers == 0) {
|
||||||
page.original_protection = 0;
|
page.original_protection = 0;
|
||||||
}
|
}
|
||||||
page.backing_writer = 0;
|
page.backing_writer = 0;
|
||||||
page.resolving = false;
|
page.resolving = false;
|
||||||
page.resolving_read_write = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
|
|
||||||
if (g_in_fault_resolution) {
|
|
||||||
FailFast("nested HandleFault call");
|
|
||||||
}
|
|
||||||
auto* region = m_impl->FindRegion(fault_vaddr);
|
|
||||||
if (region == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto& page = m_impl->GetPage(*region, fault_vaddr);
|
|
||||||
bool waited = false;
|
|
||||||
while (true) {
|
|
||||||
SpinGuard lock(page.lock);
|
|
||||||
if (access == PageFaultAccess::Read && page.late_read_pending &&
|
|
||||||
Impl::AllowsAccess(page, fault_vaddr, access)) {
|
|
||||||
page.late_read_pending = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (access == PageFaultAccess::Write && page.late_write_pending &&
|
|
||||||
Impl::AllowsAccess(page, fault_vaddr, access)) {
|
|
||||||
page.late_write_pending = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (page.resolving) {
|
|
||||||
if (page.backing_writer == CurrentThread()) {
|
|
||||||
FailFast("backing writer faulted on its own reserved page");
|
|
||||||
}
|
|
||||||
if ((!page.resolving_read_write && access != PageFaultAccess::Write) ||
|
|
||||||
(page.resolving_read_write && access != PageFaultAccess::Read &&
|
|
||||||
access != PageFaultAccess::Write)) {
|
|
||||||
FailFast("fault access is incompatible with the active resolver");
|
|
||||||
}
|
|
||||||
waited = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (page.write_watchers == 0 && page.access_watchers == 0) {
|
|
||||||
if (access != PageFaultAccess::Read && access != PageFaultAccess::Write) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool& pending = (access == PageFaultAccess::Read ? page.late_read_pending
|
|
||||||
: page.late_write_pending);
|
|
||||||
const bool allowed = Impl::AllowsAccess(page, fault_vaddr, access);
|
|
||||||
pending = false;
|
|
||||||
if (waited && !allowed) {
|
|
||||||
FailFast("page remained inaccessible after waiting for its resolver");
|
|
||||||
}
|
|
||||||
// More than one CPU can fault before a protection transition becomes visible. The first
|
|
||||||
// delayed fault consumes the hint bit; later faults must also resume once the mapped
|
|
||||||
// page already permits the requested access. A genuinely read-only/no-access page still
|
|
||||||
// falls through to the guest exception path.
|
|
||||||
return allowed;
|
|
||||||
}
|
|
||||||
if ((access != PageFaultAccess::Read && access != PageFaultAccess::Write) ||
|
|
||||||
(access == PageFaultAccess::Read && page.access_watchers == 0)) {
|
|
||||||
FailFast("fault access is incompatible with active page watchers");
|
|
||||||
}
|
|
||||||
page.resolving = true;
|
|
||||||
page.resolving_read_write = page.access_watchers != 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
g_in_fault_resolution = true;
|
|
||||||
const bool handled = m_impl->fault_handler(m_impl->fault_context, access, fault_vaddr, 1,
|
|
||||||
PageFaultPhase::Invalidate);
|
|
||||||
g_in_fault_resolution = false;
|
|
||||||
{
|
|
||||||
SpinGuard lock(page.lock);
|
|
||||||
if (!handled || !page.resolving) {
|
|
||||||
FailFast("fault invalidation did not preserve the resolving state");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_in_fault_resolution = true;
|
|
||||||
const bool completed = m_impl->fault_handler(m_impl->fault_context, access, fault_vaddr, 1,
|
|
||||||
PageFaultPhase::Complete);
|
|
||||||
g_in_fault_resolution = false;
|
|
||||||
{
|
|
||||||
SpinGuard lock(page.lock);
|
|
||||||
if (!completed || !page.resolving) {
|
|
||||||
FailFast("fault completion did not preserve the resolving state");
|
|
||||||
}
|
|
||||||
if (page.write_watchers != 0 || page.access_watchers != 0) {
|
|
||||||
const auto old_protection = Impl::WatcherProtection(page);
|
|
||||||
const bool read_only_fault = access == PageFaultAccess::Read;
|
|
||||||
if (read_only_fault && page.access_watchers == 0) {
|
|
||||||
FailFast("read fault completed without a read/write watcher");
|
|
||||||
}
|
|
||||||
page.access_watchers = 0;
|
|
||||||
if (!read_only_fault) {
|
|
||||||
page.write_watchers = 0;
|
|
||||||
}
|
|
||||||
const auto restored_protection = Impl::WatcherProtection(page);
|
|
||||||
m_impl->Protect(page, PageStart(fault_vaddr), restored_protection, old_protection,
|
|
||||||
true);
|
|
||||||
if (page.write_watchers == 0) {
|
|
||||||
page.original_protection = 0;
|
|
||||||
}
|
|
||||||
Impl::PublishDelayedFaults(page, old_protection, restored_protection);
|
|
||||||
} else if (!Impl::AllowsAccess(page, fault_vaddr, access)) {
|
|
||||||
FailFast("fault completion left the page inaccessible");
|
|
||||||
}
|
|
||||||
page.resolving = false;
|
|
||||||
page.resolving_read_write = false;
|
|
||||||
}
|
|
||||||
g_in_fault_resolution = true;
|
|
||||||
const bool released = m_impl->fault_handler(m_impl->fault_context, access, fault_vaddr, 1,
|
|
||||||
PageFaultPhase::Release);
|
|
||||||
g_in_fault_resolution = false;
|
|
||||||
if (!released) {
|
|
||||||
FailFast("fault release callback failed");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
@@ -11,11 +11,8 @@
|
|||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
enum class PageFaultAccess { Read, Write, Execute, Unknown };
|
enum class PageFaultAccess { Read, Write, Execute, Unknown };
|
||||||
enum class PageFaultPhase { Invalidate, Complete, Release };
|
|
||||||
enum class PageWatchMode { Write, ReadWrite };
|
enum class PageWatchMode { Write, ReadWrite };
|
||||||
|
|
||||||
using PageFaultHandler = bool (*)(void* context, PageFaultAccess access, uint64_t vaddr,
|
|
||||||
uint64_t size, PageFaultPhase phase) noexcept;
|
|
||||||
class PageManager final {
|
class PageManager final {
|
||||||
public:
|
public:
|
||||||
class BackingWrite final {
|
class BackingWrite final {
|
||||||
@@ -30,21 +27,19 @@ public:
|
|||||||
uint64_t m_size = 0;
|
uint64_t m_size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
PageManager(PageFaultHandler fault_handler, void* fault_context);
|
PageManager();
|
||||||
// The owner must stop all PageManager callers before destruction.
|
// The owner must stop all PageManager callers before destruction.
|
||||||
~PageManager();
|
~PageManager();
|
||||||
|
|
||||||
KYTY_CLASS_NO_COPY(PageManager);
|
KYTY_CLASS_NO_COPY(PageManager);
|
||||||
|
|
||||||
[[nodiscard]] uint64_t GetPageSize() const;
|
[[nodiscard]] uint64_t GetPageSize() const;
|
||||||
[[nodiscard]] bool IsTracked(uint64_t vaddr) const noexcept;
|
|
||||||
|
|
||||||
void UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
|
void UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
|
||||||
PageWatchMode mode = PageWatchMode::Write);
|
PageWatchMode mode = PageWatchMode::Write);
|
||||||
void OnGpuMap(uint64_t vaddr, uint64_t size);
|
void OnGpuMap(uint64_t vaddr, uint64_t size);
|
||||||
void OnGpuUnmap(uint64_t vaddr, uint64_t size);
|
void OnGpuUnmap(uint64_t vaddr, uint64_t size);
|
||||||
|
|
||||||
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
|
|
||||||
[[nodiscard]] std::vector<std::unique_ptr<BackingWrite>>
|
[[nodiscard]] std::vector<std::unique_ptr<BackingWrite>>
|
||||||
ReserveBackingWrites(std::span<const RangeSet::Range> ranges);
|
ReserveBackingWrites(std::span<const RangeSet::Range> ranges);
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
enum class CpuFaultAction { Untracked, Continue, Download };
|
|
||||||
|
|
||||||
class TrackingSpinLock final {
|
class TrackingSpinLock final {
|
||||||
public:
|
public:
|
||||||
void lock() noexcept {
|
void lock() noexcept {
|
||||||
@@ -85,18 +83,6 @@ public:
|
|||||||
KYTY_CLASS_NO_COPY(RegionManager);
|
KYTY_CLASS_NO_COPY(RegionManager);
|
||||||
|
|
||||||
[[nodiscard]] uint64_t GetCpuAddr() const { return m_cpu_addr; }
|
[[nodiscard]] uint64_t GetCpuAddr() const { return m_cpu_addr; }
|
||||||
void Track(uint64_t vaddr, uint64_t size) {
|
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
m_tracked.set(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Untrack(uint64_t vaddr, uint64_t size) {
|
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
m_tracked.reset(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <DirtySource source>
|
template <DirtySource source>
|
||||||
[[nodiscard]] bool IsModified(uint64_t offset, uint64_t size) const {
|
[[nodiscard]] bool IsModified(uint64_t offset, uint64_t size) const {
|
||||||
const auto [start, end] = GetPageRange(m_cpu_addr + offset, size);
|
const auto [start, end] = GetPageRange(m_cpu_addr + offset, size);
|
||||||
@@ -126,15 +112,15 @@ public:
|
|||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
const auto [start, end] = GetPageRange(vaddr, size);
|
||||||
if constexpr (source == DirtySource::Cpu && enable) {
|
if constexpr (source == DirtySource::Cpu && enable) {
|
||||||
for (auto page = start; page < end; page++) {
|
for (auto page = start; page < end; page++) {
|
||||||
if (m_gpu_dirty.test(page) || m_fault_pending.test(page)) {
|
if (m_gpu_dirty.test(page)) {
|
||||||
EXIT("CPU dirty state conflicts with GPU dirty or pending fault state\n");
|
EXIT("CPU dirty state conflicts with GPU dirty state\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (source == DirtySource::Gpu && enable) {
|
if constexpr (source == DirtySource::Gpu && enable) {
|
||||||
for (auto page = start; page < end; page++) {
|
for (auto page = start; page < end; page++) {
|
||||||
if (m_cpu_dirty.test(page) || m_fault_pending.test(page)) {
|
if (m_cpu_dirty.test(page)) {
|
||||||
EXIT("GPU dirty state conflicts with CPU dirty or pending fault state\n");
|
EXIT("GPU dirty state conflicts with CPU dirty state\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,110 +137,10 @@ public:
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] CpuFaultAction BeginCpuFault(uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultAccess access = PageFaultAccess::Write) {
|
|
||||||
if (access != PageFaultAccess::Read && access != PageFaultAccess::Write) {
|
|
||||||
EXIT("unsupported CPU fault access while beginning ownership transfer\n");
|
|
||||||
}
|
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
|
||||||
const bool tracked = m_tracked.test(start);
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
if (m_tracked.test(page) != tracked) {
|
|
||||||
EXIT("CPU fault spans mixed tracked and untracked pages\n");
|
|
||||||
}
|
|
||||||
if (m_fault_pending.test(page)) {
|
|
||||||
return CpuFaultAction::Untracked;
|
|
||||||
}
|
|
||||||
if (m_cpu_dirty.test(page) != m_writable.test(page) ||
|
|
||||||
(m_gpu_dirty.test(page) && (m_cpu_dirty.test(page) || m_writable.test(page)))) {
|
|
||||||
EXIT("inconsistent CPU fault page state\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!tracked) {
|
|
||||||
return CpuFaultAction::Untracked;
|
|
||||||
}
|
|
||||||
bool gpu_dirty = m_gpu_dirty.test(start);
|
|
||||||
bool writable = m_writable.test(start);
|
|
||||||
for (auto page = start + 1; page < end; page++) {
|
|
||||||
if (m_gpu_dirty.test(page) != gpu_dirty || m_writable.test(page) != writable) {
|
|
||||||
EXIT("CPU fault spans pages with incompatible dirty or writable state\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
if (!gpu_dirty && access == PageFaultAccess::Write) {
|
|
||||||
m_cpu_dirty.set(page);
|
|
||||||
m_writable.set(page);
|
|
||||||
}
|
|
||||||
m_fault_pending.set(page);
|
|
||||||
}
|
|
||||||
return gpu_dirty ? CpuFaultAction::Download : CpuFaultAction::Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool CompleteCpuFault(uint64_t vaddr, uint64_t size, PageFaultAccess access,
|
|
||||||
bool downloaded) {
|
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
if (!m_fault_pending.test(page)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
const bool gpu_dirty = m_gpu_dirty.test(page);
|
|
||||||
if (gpu_dirty != downloaded) {
|
|
||||||
EXIT("CPU fault download result disagrees with GPU dirty state\n");
|
|
||||||
}
|
|
||||||
if (gpu_dirty) {
|
|
||||||
m_gpu_dirty.reset(page);
|
|
||||||
switch (access) {
|
|
||||||
case PageFaultAccess::Read: break;
|
|
||||||
case PageFaultAccess::Write:
|
|
||||||
m_cpu_dirty.set(page);
|
|
||||||
m_writable.set(page);
|
|
||||||
break;
|
|
||||||
default: EXIT("unsupported CPU fault access after GPU download\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_fault_pending.reset(page);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasPendingFault(uint64_t vaddr, uint64_t size) const {
|
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
if (m_fault_pending.test(page)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool CompleteVirtualGpuWrite(uint64_t vaddr, uint64_t size) {
|
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
if (!m_fault_pending.test(page)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!m_gpu_dirty.test(page)) {
|
|
||||||
EXIT("virtual GPU write completion found a non-GPU-dirty page\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto page = start; page < end; page++) {
|
|
||||||
m_gpu_dirty.reset(page);
|
|
||||||
m_cpu_dirty.set(page);
|
|
||||||
m_writable.set(page);
|
|
||||||
m_fault_pending.reset(page);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <DirtySource source, bool clear, typename Func>
|
template <DirtySource source, bool clear, typename Func>
|
||||||
RegionBits ForEachModifiedRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
RegionBits ForEachModifiedRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
||||||
const auto [start, end] = GetPageRange(vaddr, size);
|
const auto [start, end] = GetPageRange(vaddr, size);
|
||||||
auto mask = GetBits<source>();
|
auto mask = GetBits<source>();
|
||||||
if constexpr (source == DirtySource::Cpu) {
|
|
||||||
mask &= ~m_fault_pending;
|
|
||||||
}
|
|
||||||
for (auto page = 0u; page < start; page++) {
|
for (auto page = 0u; page < start; page++) {
|
||||||
mask.reset(page);
|
mask.reset(page);
|
||||||
}
|
}
|
||||||
@@ -351,8 +237,6 @@ private:
|
|||||||
RegionBits m_cpu_dirty;
|
RegionBits m_cpu_dirty;
|
||||||
RegionBits m_gpu_dirty;
|
RegionBits m_gpu_dirty;
|
||||||
RegionBits m_writable;
|
RegionBits m_writable;
|
||||||
RegionBits m_fault_pending;
|
|
||||||
RegionBits m_tracked;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
+2
-110
@@ -123,24 +123,6 @@ struct BufferCache::RetiredBuffer {
|
|||||||
std::shared_ptr<Buffer> owner;
|
std::shared_ptr<Buffer> owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BufferCache::FaultReadback {
|
|
||||||
PageFaultAccess access = PageFaultAccess::Unknown;
|
|
||||||
uint64_t vaddr = 0;
|
|
||||||
uint64_t size = 0;
|
|
||||||
std::vector<DownloadRange> ranges;
|
|
||||||
bool installed = false;
|
|
||||||
|
|
||||||
[[nodiscard]] bool Active() const noexcept { return !ranges.empty(); }
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
access = PageFaultAccess::Unknown;
|
|
||||||
vaddr = 0;
|
|
||||||
size = 0;
|
|
||||||
installed = false;
|
|
||||||
ranges.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BufferCache::PendingBackingPublication {
|
struct BufferCache::PendingBackingPublication {
|
||||||
uint64_t address = 0;
|
uint64_t address = 0;
|
||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
@@ -253,7 +235,7 @@ BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
|||||||
ResourceMutex& resource_mutex)
|
ResourceMutex& resource_mutex)
|
||||||
: m_graphics(graphics), m_scheduler(scheduler),
|
: m_graphics(graphics), m_scheduler(scheduler),
|
||||||
m_gds_buffer(graphics, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize),
|
m_gds_buffer(graphics, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize),
|
||||||
m_fault_readback(std::make_unique<FaultReadback>()), m_memory_tracker(page_manager),
|
m_memory_tracker(page_manager),
|
||||||
m_staging_buffer(graphics, scheduler, MemoryUsage::Upload, 512 * MiB),
|
m_staging_buffer(graphics, scheduler, MemoryUsage::Upload, 512 * MiB),
|
||||||
m_stream_buffer(graphics, scheduler, MemoryUsage::Stream, 64 * MiB),
|
m_stream_buffer(graphics, scheduler, MemoryUsage::Stream, 64 * MiB),
|
||||||
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 32 * MiB),
|
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 32 * MiB),
|
||||||
@@ -277,9 +259,6 @@ BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BufferCache::~BufferCache() {
|
BufferCache::~BufferCache() {
|
||||||
if (m_fault_readback->Active()) {
|
|
||||||
EXIT("BufferCache: destroyed with an active fault readback\n");
|
|
||||||
}
|
|
||||||
if (!m_gpu_modified_ranges.Empty()) {
|
if (!m_gpu_modified_ranges.Empty()) {
|
||||||
EXIT("BufferCache: destroyed with pending GPU-modified ranges\n");
|
EXIT("BufferCache: destroyed with pending GPU-modified ranges\n");
|
||||||
}
|
}
|
||||||
@@ -447,93 +426,6 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCache::InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept {
|
|
||||||
const auto page = vaddr & ~(TRACKER_PAGE_SIZE - 1);
|
|
||||||
if (size == 0 || size > page + TRACKER_PAGE_SIZE - vaddr) {
|
|
||||||
EXIT("BufferCache: invalid page-fault range\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phase == PageFaultPhase::Complete) {
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
auto& fault = *m_fault_readback;
|
|
||||||
if (!fault.Active()) {
|
|
||||||
return m_memory_tracker.CompleteCpuFault(vaddr, size, access, false);
|
|
||||||
}
|
|
||||||
if (fault.access != access || fault.vaddr != vaddr || fault.size != size ||
|
|
||||||
fault.installed) {
|
|
||||||
EXIT("BufferCache: mismatched fault readback completion\n");
|
|
||||||
}
|
|
||||||
PublishDownloads(fault.ranges);
|
|
||||||
if (!m_memory_tracker.CompleteCpuFault(vaddr, size, access, true)) {
|
|
||||||
EXIT("BufferCache: failed to complete downloaded CPU fault\n");
|
|
||||||
}
|
|
||||||
fault.installed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phase == PageFaultPhase::Release) {
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
auto& fault = *m_fault_readback;
|
|
||||||
if (fault.Active()) {
|
|
||||||
if (fault.access != access || fault.vaddr != vaddr || fault.size != size ||
|
|
||||||
!fault.installed) {
|
|
||||||
EXIT("BufferCache: mismatched fault readback release\n");
|
|
||||||
}
|
|
||||||
for (const auto& range: fault.ranges) {
|
|
||||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
|
||||||
}
|
|
||||||
fault.Reset();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phase != PageFaultPhase::Invalidate) {
|
|
||||||
EXIT("BufferCache: unsupported page-fault phase\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto action = m_memory_tracker.BeginCpuFault(vaddr, size, access);
|
|
||||||
if (action != CpuFaultAction::Download) {
|
|
||||||
return action == CpuFaultAction::Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& fault = *m_fault_readback;
|
|
||||||
std::vector<DownloadCopy> copies;
|
|
||||||
{
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
if (fault.Active()) {
|
|
||||||
EXIT("BufferCache: nested fault readback\n");
|
|
||||||
}
|
|
||||||
fault.access = access;
|
|
||||||
fault.vaddr = vaddr;
|
|
||||||
fault.size = size;
|
|
||||||
|
|
||||||
m_gpu_modified_ranges.ForEachIntersection(
|
|
||||||
page, TRACKER_PAGE_SIZE, [&](RangeSet::Range range) {
|
|
||||||
auto owner = m_buffers.upper_bound(range.address);
|
|
||||||
if (owner == m_buffers.begin()) {
|
|
||||||
EXIT("BufferCache: fault readback has no buffer owner\n");
|
|
||||||
}
|
|
||||||
--owner;
|
|
||||||
auto& cached = *owner->second;
|
|
||||||
if (!cached.buffer->IsInBounds(range.address, range.size)) {
|
|
||||||
EXIT("BufferCache: fault readback is outside its buffer owner\n");
|
|
||||||
}
|
|
||||||
copies.push_back({cached.buffer, cached.buffer->Offset(range.address),
|
|
||||||
range.address, range.size});
|
|
||||||
});
|
|
||||||
if (copies.empty()) {
|
|
||||||
EXIT("BufferCache: GPU-dirty fault page has no dirty byte ranges\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fault.ranges = RecordDownloads(copies);
|
|
||||||
if (!fault.Active()) {
|
|
||||||
EXIT("BufferCache: GPU-dirty fault page has no dirty byte ranges\n");
|
|
||||||
}
|
|
||||||
m_scheduler.FinishCurrent();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
||||||
if (vaddr == 0 || size == 0 || size > UINT64_MAX - vaddr) {
|
if (vaddr == 0 || size == 0 || size > UINT64_MAX - vaddr) {
|
||||||
EXIT("BufferCache: invalid unmap range\n");
|
EXIT("BufferCache: invalid unmap range\n");
|
||||||
@@ -1201,7 +1093,7 @@ void BufferCache::RunGarbageCollector() {
|
|||||||
if (m_graphics.CanReportMemoryUsage()) {
|
if (m_graphics.CanReportMemoryUsage()) {
|
||||||
m_total_used_memory = m_graphics.GetDeviceMemoryUsage();
|
m_total_used_memory = m_graphics.GetDeviceMemoryUsage();
|
||||||
}
|
}
|
||||||
if (m_total_used_memory < m_trigger_gc_memory || m_fault_readback->Active()) {
|
if (m_total_used_memory < m_trigger_gc_memory) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-7
@@ -47,11 +47,9 @@ public:
|
|||||||
~BufferCache();
|
~BufferCache();
|
||||||
KYTY_CLASS_NO_COPY(BufferCache);
|
KYTY_CLASS_NO_COPY(BufferCache);
|
||||||
|
|
||||||
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||||
PageFaultPhase phase) noexcept;
|
void ReadMemory(uint64_t vaddr, uint64_t size);
|
||||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||||
void ReadMemory(uint64_t vaddr, uint64_t size);
|
|
||||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
|
||||||
[[nodiscard]] BufferBinding ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
|
[[nodiscard]] BufferBinding ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
|
||||||
bool is_written = false, bool is_read = true,
|
bool is_written = false, bool is_read = true,
|
||||||
bool is_formatted = false);
|
bool is_formatted = false);
|
||||||
@@ -90,7 +88,6 @@ private:
|
|||||||
struct DownloadCopy;
|
struct DownloadCopy;
|
||||||
struct DownloadRange;
|
struct DownloadRange;
|
||||||
struct RetiredBuffer;
|
struct RetiredBuffer;
|
||||||
struct FaultReadback;
|
|
||||||
struct PendingBackingPublication;
|
struct PendingBackingPublication;
|
||||||
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
|
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
|
||||||
[[nodiscard]] static uint64_t AlignDown(uint64_t value) noexcept;
|
[[nodiscard]] static uint64_t AlignDown(uint64_t value) noexcept;
|
||||||
@@ -120,7 +117,6 @@ private:
|
|||||||
Common::Mutex m_mutex;
|
Common::Mutex m_mutex;
|
||||||
std::shared_ptr<Buffer> m_null_buffer;
|
std::shared_ptr<Buffer> m_null_buffer;
|
||||||
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
||||||
std::unique_ptr<FaultReadback> m_fault_readback;
|
|
||||||
RangeSet m_gpu_modified_ranges;
|
RangeSet m_gpu_modified_ranges;
|
||||||
RangeSet m_image_invalidated_ranges;
|
RangeSet m_image_invalidated_ranges;
|
||||||
std::mutex m_publication_mutex;
|
std::mutex m_publication_mutex;
|
||||||
|
|||||||
+1
-23
@@ -7,33 +7,11 @@
|
|||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler)
|
GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||||
: m_page_manager(FaultThunk, this),
|
: m_buffer_cache(graphics, scheduler, m_page_manager, m_texture_cache, m_resource_mutex),
|
||||||
m_buffer_cache(graphics, scheduler, m_page_manager, m_texture_cache, m_resource_mutex),
|
|
||||||
m_texture_cache(graphics, scheduler, m_page_manager, m_buffer_cache, m_resource_mutex) {}
|
m_texture_cache(graphics, scheduler, m_page_manager, m_buffer_cache, m_resource_mutex) {}
|
||||||
|
|
||||||
GpuResourceManager::~GpuResourceManager() = default;
|
GpuResourceManager::~GpuResourceManager() = default;
|
||||||
|
|
||||||
bool GpuResourceManager::FaultThunk(void* context, PageFaultAccess access, uint64_t vaddr,
|
|
||||||
uint64_t size, PageFaultPhase phase) noexcept {
|
|
||||||
return static_cast<GpuResourceManager*>(context)->InvalidateMemory(access, vaddr, size, phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GpuResourceManager::InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept {
|
|
||||||
// Let the authoritative image materialize first. A clean overlapping buffer marks a write
|
|
||||||
// fault CPU-dirty when it begins ownership transfer; doing that before image preflight would
|
|
||||||
// make the image appear to race a real CPU write. Completion and release retain buffer-first
|
|
||||||
// ordering so its pending fault is gone before TextureCache publishes the downloaded backing.
|
|
||||||
if (phase == PageFaultPhase::Invalidate) {
|
|
||||||
const bool image_handled = m_texture_cache.InvalidateMemory(access, vaddr, size, phase);
|
|
||||||
const bool buffer_handled = m_buffer_cache.InvalidateMemory(access, vaddr, size, phase);
|
|
||||||
return buffer_handled || image_handled;
|
|
||||||
}
|
|
||||||
const bool buffer_handled = m_buffer_cache.InvalidateMemory(access, vaddr, size, phase);
|
|
||||||
const bool image_handled = m_texture_cache.InvalidateMemory(access, vaddr, size, phase);
|
|
||||||
return buffer_handled || image_handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
|
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
|
||||||
constexpr uint64_t fault_size = 8;
|
constexpr uint64_t fault_size = 8;
|
||||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||||
|
|||||||
@@ -34,11 +34,6 @@ public:
|
|||||||
void RunGarbageCollector();
|
void RunGarbageCollector();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool FaultThunk(void* context, PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept;
|
|
||||||
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept;
|
|
||||||
|
|
||||||
PageManager m_page_manager;
|
PageManager m_page_manager;
|
||||||
ResourceMutex m_resource_mutex;
|
ResourceMutex m_resource_mutex;
|
||||||
BufferCache m_buffer_cache;
|
BufferCache m_buffer_cache;
|
||||||
|
|||||||
@@ -1826,37 +1826,6 @@ bool TextureCache::TouchMeta(uint64_t address, uint32_t slice, bool is_clear) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureCache::InvalidateMemory(PageFaultAccess access, uint64_t address, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept {
|
|
||||||
if ((access != PageFaultAccess::Read && access != PageFaultAccess::Write) ||
|
|
||||||
!GuestRange {address, size}.Valid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (access == PageFaultAccess::Read) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (phase == PageFaultPhase::Invalidate) {
|
|
||||||
CacheLock lock(*this, m_lock);
|
|
||||||
const bool tracked =
|
|
||||||
std::ranges::any_of(FindImagesInRegion(address, size, true), [&](ImageId id) {
|
|
||||||
const auto owner = ResolveOwner(id);
|
|
||||||
return owner != nullptr && !owner->depth_id && owner->IsTracked();
|
|
||||||
});
|
|
||||||
if (tracked) {
|
|
||||||
InvalidateCpuAliases(address, size);
|
|
||||||
}
|
|
||||||
return tracked;
|
|
||||||
}
|
|
||||||
if (phase != PageFaultPhase::Complete && phase != PageFaultPhase::Release) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CacheLock lock(*this, m_lock);
|
|
||||||
return std::ranges::any_of(FindImagesInRegion(address, size, true), [&](ImageId id) {
|
|
||||||
const auto owner = ResolveOwner(id);
|
|
||||||
return owner != nullptr && !owner->depth_id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
|
void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
|
||||||
if (!GuestRange {address, size}.Valid()) {
|
if (!GuestRange {address, size}.Valid()) {
|
||||||
EXIT("TextureCache: invalid unmap range\n");
|
EXIT("TextureCache: invalid unmap range\n");
|
||||||
|
|||||||
+3
-5
@@ -76,11 +76,9 @@ public:
|
|||||||
[[nodiscard]] bool ClearMeta(uint64_t address);
|
[[nodiscard]] bool ClearMeta(uint64_t address);
|
||||||
[[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear);
|
[[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear);
|
||||||
|
|
||||||
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t address, uint64_t size,
|
void UnmapMemory(uint64_t address, uint64_t size);
|
||||||
PageFaultPhase phase) noexcept;
|
void ProcessDownloadImages();
|
||||||
void UnmapMemory(uint64_t address, uint64_t size);
|
void RunGarbageCollector();
|
||||||
void ProcessDownloadImages();
|
|
||||||
void RunGarbageCollector();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class TransferDirection { Upload, Download };
|
enum class TransferDirection { Upload, Download };
|
||||||
|
|||||||
+20
-24
@@ -18,6 +18,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -805,15 +806,15 @@ private:
|
|||||||
Common::Mutex m_mutex;
|
Common::Mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
static PhysicalMemory* g_physical_memory = nullptr;
|
static std::unique_ptr<PhysicalMemory> g_physical_memory;
|
||||||
static FlexibleMemory* g_flexible_memory = nullptr;
|
static std::unique_ptr<FlexibleMemory> g_flexible_memory;
|
||||||
static PooledMemory* g_pooled_memory = nullptr;
|
static std::unique_ptr<PooledMemory> g_pooled_memory;
|
||||||
static VirtualRanges* g_virtual_ranges = nullptr;
|
static std::unique_ptr<VirtualRanges> g_virtual_ranges;
|
||||||
static GuestAddressSpace* g_guest_address_space = nullptr;
|
static std::unique_ptr<GuestAddressSpace> g_guest_address_space;
|
||||||
static callback_func_t g_alloc_callback = nullptr;
|
static callback_func_t g_alloc_callback = nullptr;
|
||||||
static callback_func_t g_free_callback = nullptr;
|
static callback_func_t g_free_callback = nullptr;
|
||||||
static std::atomic<uint64_t> g_memory_pool_committed = 0;
|
static std::atomic<uint64_t> g_memory_pool_committed = 0;
|
||||||
static void MemoryPoolSubtractCommitted(uint64_t len);
|
static void MemoryPoolSubtractCommitted(uint64_t len);
|
||||||
// Keep host mappings, physical blocks, placeholders, and virtual ranges in step.
|
// Keep host mappings, physical blocks, placeholders, and virtual ranges in step.
|
||||||
static std::recursive_mutex g_memory_operation_mutex;
|
static std::recursive_mutex g_memory_operation_mutex;
|
||||||
|
|
||||||
@@ -970,11 +971,11 @@ static bool ReplaceFixedRangeWithReserved(uint64_t start, uint64_t size);
|
|||||||
KYTY_SUBSYSTEM_INIT(Memory) {
|
KYTY_SUBSYSTEM_INIT(Memory) {
|
||||||
g_flexible_memory_size_frozen = true;
|
g_flexible_memory_size_frozen = true;
|
||||||
VirtualMemory::Init();
|
VirtualMemory::Init();
|
||||||
g_guest_address_space = new GuestAddressSpace(PhysicalMemory::TotalSize());
|
g_guest_address_space = std::make_unique<GuestAddressSpace>(PhysicalMemory::TotalSize());
|
||||||
g_physical_memory = new PhysicalMemory;
|
g_physical_memory = std::make_unique<PhysicalMemory>();
|
||||||
g_flexible_memory = new FlexibleMemory;
|
g_flexible_memory = std::make_unique<FlexibleMemory>();
|
||||||
g_pooled_memory = new PooledMemory;
|
g_pooled_memory = std::make_unique<PooledMemory>();
|
||||||
g_virtual_ranges = new VirtualRanges;
|
g_virtual_ranges = std::make_unique<VirtualRanges>();
|
||||||
EXIT_IF(!g_guest_address_space->SelfTest());
|
EXIT_IF(!g_guest_address_space->SelfTest());
|
||||||
EXIT_IF(!SelfTestSub64SharedPlaceholderAlias());
|
EXIT_IF(!SelfTestSub64SharedPlaceholderAlias());
|
||||||
}
|
}
|
||||||
@@ -982,16 +983,11 @@ KYTY_SUBSYSTEM_INIT(Memory) {
|
|||||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {}
|
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {}
|
||||||
|
|
||||||
KYTY_SUBSYSTEM_DESTROY(Memory) {
|
KYTY_SUBSYSTEM_DESTROY(Memory) {
|
||||||
delete g_pooled_memory;
|
g_pooled_memory.reset();
|
||||||
g_pooled_memory = nullptr;
|
g_flexible_memory.reset();
|
||||||
delete g_flexible_memory;
|
g_physical_memory.reset();
|
||||||
g_flexible_memory = nullptr;
|
g_virtual_ranges.reset();
|
||||||
delete g_physical_memory;
|
g_guest_address_space.reset();
|
||||||
g_physical_memory = nullptr;
|
|
||||||
delete g_virtual_ranges;
|
|
||||||
g_virtual_ranges = nullptr;
|
|
||||||
delete g_guest_address_space;
|
|
||||||
g_guest_address_space = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AlignedPos {
|
struct AlignedPos {
|
||||||
|
|||||||
+347
-1100
File diff suppressed because it is too large
Load Diff
+266
-656
File diff suppressed because it is too large
Load Diff
@@ -3135,10 +3135,7 @@ public:
|
|||||||
Require(name, "partial image unmap tracking",
|
Require(name, "partial image unmap tracking",
|
||||||
partial_unmap_image_id &&
|
partial_unmap_image_id &&
|
||||||
!texture_cache.FindImageFromRange(partial_unmap_image.info.data.address,
|
!texture_cache.FindImageFromRange(partial_unmap_image.info.data.address,
|
||||||
0x2000, false) &&
|
0x2000, false),
|
||||||
!texture_cache.InvalidateMemory(
|
|
||||||
PageFaultAccess::Write, partial_unmap_image.info.data.address + 0x1000,
|
|
||||||
0x1000, PageFaultPhase::Invalidate),
|
|
||||||
"partial unmap left the deleted image's mapped tail tracked");
|
"partial unmap left the deleted image's mapped tail tracked");
|
||||||
|
|
||||||
constexpr uint64_t unformatted_alias_offset = 0x2500000;
|
constexpr uint64_t unformatted_alias_offset = 0x2500000;
|
||||||
@@ -14459,17 +14456,6 @@ void CheckEmbeddedFetchVertexOffset() {
|
|||||||
std::printf("[host] %-32s ok\n", "EmbeddedFetchVertexOffset");
|
std::printf("[host] %-32s ok\n", "EmbeddedFetchVertexOffset");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CacheFaultContext {
|
|
||||||
TextureCache* texture = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CacheFault(void* opaque, PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
|
||||||
PageFaultPhase phase) noexcept {
|
|
||||||
auto* context = static_cast<CacheFaultContext*>(opaque);
|
|
||||||
return context != nullptr && context->texture != nullptr &&
|
|
||||||
context->texture->InvalidateMemory(access, vaddr, size, phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void RunReverseRenderTargetDeathCase() {
|
[[noreturn]] void RunReverseRenderTargetDeathCase() {
|
||||||
(void)TextureGetRenderTargetFormat(12u, 7u, 3u);
|
(void)TextureGetRenderTargetFormat(12u, 7u, 3u);
|
||||||
std::_Exit(0x7f);
|
std::_Exit(0x7f);
|
||||||
@@ -16110,7 +16096,7 @@ void CheckStorageTextureGpuOwnedRebindState() {
|
|||||||
auto* memory = reinterpret_cast<uint8_t*>(guest_memory);
|
auto* memory = reinterpret_cast<uint8_t*>(guest_memory);
|
||||||
Require("StorageTextureGpuOwnedRebind", "allocation", guest_memory == base,
|
Require("StorageTextureGpuOwnedRebind", "allocation", guest_memory == base,
|
||||||
"fixed guest-owner allocation failed");
|
"fixed guest-owner allocation failed");
|
||||||
PageManager page_manager(CacheFault, nullptr);
|
PageManager page_manager;
|
||||||
MemoryTracker tracker(page_manager);
|
MemoryTracker tracker(page_manager);
|
||||||
page_manager.OnGpuMap(base, size);
|
page_manager.OnGpuMap(base, size);
|
||||||
tracker.ForEachUploadRange(
|
tracker.ForEachUploadRange(
|
||||||
|
|||||||
Reference in New Issue
Block a user