mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00: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 =
|
||||
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||
manager->ApplyProtection(changed, false);
|
||||
manager->Untrack(manager->GetCpuAddr() + offset, bytes);
|
||||
});
|
||||
locks.clear();
|
||||
}
|
||||
@@ -168,93 +167,4 @@ void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t 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
|
||||
|
||||
@@ -30,13 +30,6 @@ public:
|
||||
void MarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
||||
void UnmarkRegionAsGpuModified(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>
|
||||
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) {
|
||||
static_assert(std::is_invocable_v<Flush&>);
|
||||
@@ -78,10 +71,8 @@ public:
|
||||
EXIT("memory invalidation retained GPU-owned pages\n");
|
||||
}
|
||||
}
|
||||
[[nodiscard]] bool InvalidateVirtualGpuWrite(PageFaultAccess access, uint64_t vaddr,
|
||||
uint64_t size, PageFaultPhase phase) noexcept;
|
||||
void ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
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,
|
||||
const char* operation);
|
||||
|
||||
@@ -102,9 +93,6 @@ public:
|
||||
}
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
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,
|
||||
preflight);
|
||||
});
|
||||
@@ -141,7 +129,6 @@ public:
|
||||
const auto* previous_upload_owner = std::exchange(s_upload_owner, this);
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
manager->lock.lock();
|
||||
manager->Track(manager->GetCpuAddr() + offset, bytes);
|
||||
manager->ForEachModifiedRange<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset,
|
||||
bytes, range_func);
|
||||
if (!is_written) {
|
||||
|
||||
@@ -56,8 +56,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
// Zero is the unknown protection sentinel.
|
||||
constexpr uint32_t UNKNOWN_PROTECTION = 0;
|
||||
|
||||
thread_local bool g_in_fault_resolution = false;
|
||||
|
||||
[[noreturn]] void FailFast(const char* reason = nullptr) noexcept {
|
||||
std::fputs("PageManager fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
||||
@@ -162,11 +160,8 @@ struct PageManager::Impl {
|
||||
uint32_t original_protection = 0;
|
||||
uint32_t backing_writer = 0;
|
||||
// Shadow the protection applied through Protect().
|
||||
uint32_t current_protection = UNKNOWN_PROTECTION;
|
||||
bool resolving = false;
|
||||
bool resolving_read_write = false;
|
||||
bool late_read_pending = false;
|
||||
bool late_write_pending = false;
|
||||
uint32_t current_protection = UNKNOWN_PROTECTION;
|
||||
bool resolving = false;
|
||||
};
|
||||
|
||||
struct Region {
|
||||
@@ -193,10 +188,7 @@ struct PageManager::Impl {
|
||||
std::span<PageState*> m_pages;
|
||||
};
|
||||
|
||||
Impl(PageFaultHandler handler, void* context): fault_handler(handler), fault_context(context) {
|
||||
if (fault_handler == nullptr) {
|
||||
Fatal("null page-manager fault callback");
|
||||
}
|
||||
Impl() {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
SYSTEM_INFO info {};
|
||||
GetSystemInfo(&info);
|
||||
@@ -268,17 +260,6 @@ struct PageManager::Impl {
|
||||
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) {
|
||||
for (auto* page: pages) {
|
||||
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,
|
||||
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;
|
||||
if (pages.size() != expected_old.size()) {
|
||||
FailFast("protection range state size mismatch");
|
||||
@@ -306,9 +276,6 @@ struct PageManager::Impl {
|
||||
for (size_t i = 0; i < pages.size(); i++) {
|
||||
const auto actual = pages[i]->current_protection;
|
||||
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
|
||||
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
|
||||
vaddr + i * PAGE_SIZE, actual, expected_old[i], protection);
|
||||
@@ -316,9 +283,6 @@ struct PageManager::Impl {
|
||||
}
|
||||
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size,
|
||||
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,
|
||||
protection);
|
||||
}
|
||||
@@ -327,47 +291,28 @@ struct PageManager::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void Protect(PageState& page, uint64_t vaddr, uint32_t protection, uint32_t expected_old,
|
||||
bool fault_path) noexcept {
|
||||
void Protect(PageState& page, uint64_t vaddr, uint32_t protection,
|
||||
uint32_t expected_old) noexcept {
|
||||
PageState* pages[] = {&page};
|
||||
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::vector<std::unique_ptr<Region>> region_storage;
|
||||
std::mutex region_mutex;
|
||||
PageFaultHandler fault_handler = nullptr;
|
||||
void* fault_context = nullptr;
|
||||
};
|
||||
|
||||
static_assert(std::atomic<void*>::is_always_lock_free);
|
||||
|
||||
PageManager::PageManager(PageFaultHandler fault_handler, void* fault_context)
|
||||
: m_impl(std::make_unique<Impl>(fault_handler, fault_context)) {}
|
||||
PageManager::PageManager(): m_impl(std::make_unique<Impl>()) {}
|
||||
|
||||
PageManager::~PageManager() = default;
|
||||
|
||||
uint64_t PageManager::GetPageSize() const {
|
||||
if (g_in_fault_resolution) {
|
||||
FailFast("nested page fault while resolving a watched page");
|
||||
}
|
||||
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,
|
||||
PageWatchMode mode) {
|
||||
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),
|
||||
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;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < page_count; i++) {
|
||||
auto& page = *pages[i];
|
||||
const auto protection = new_protections[i];
|
||||
if (track) {
|
||||
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;
|
||||
}
|
||||
for (auto* page: pages) {
|
||||
if (!track && page->backing_writer == 0 && page->write_watchers == 0 &&
|
||||
page->access_watchers == 0) {
|
||||
page->original_protection = 0;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if (g_in_fault_resolution) {
|
||||
FailFast("backing write began during fault resolution");
|
||||
}
|
||||
const auto end = PageEnd(vaddr, size);
|
||||
const auto writer = CurrentThread();
|
||||
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) {
|
||||
Fatal("backing write races page resolution at 0x%016" PRIx64, address);
|
||||
}
|
||||
page.resolving = true;
|
||||
page.resolving_read_write = true;
|
||||
page.backing_writer = writer;
|
||||
page.resolving = true;
|
||||
page.backing_writer = writer;
|
||||
}
|
||||
}
|
||||
|
||||
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 writer = CurrentThread();
|
||||
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 new_protection = Impl::WatcherProtection(page);
|
||||
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) {
|
||||
page.original_protection = 0;
|
||||
}
|
||||
page.backing_writer = 0;
|
||||
page.resolving = false;
|
||||
page.resolving_read_write = false;
|
||||
page.backing_writer = 0;
|
||||
page.resolving = 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
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
namespace Libs::Graphics {
|
||||
|
||||
enum class PageFaultAccess { Read, Write, Execute, Unknown };
|
||||
enum class PageFaultPhase { Invalidate, Complete, Release };
|
||||
enum class PageWatchMode { Write, ReadWrite };
|
||||
|
||||
using PageFaultHandler = bool (*)(void* context, PageFaultAccess access, uint64_t vaddr,
|
||||
uint64_t size, PageFaultPhase phase) noexcept;
|
||||
class PageManager final {
|
||||
public:
|
||||
class BackingWrite final {
|
||||
@@ -30,21 +27,19 @@ public:
|
||||
uint64_t m_size = 0;
|
||||
};
|
||||
|
||||
PageManager(PageFaultHandler fault_handler, void* fault_context);
|
||||
PageManager();
|
||||
// The owner must stop all PageManager callers before destruction.
|
||||
~PageManager();
|
||||
|
||||
KYTY_CLASS_NO_COPY(PageManager);
|
||||
|
||||
[[nodiscard]] uint64_t GetPageSize() const;
|
||||
[[nodiscard]] bool IsTracked(uint64_t vaddr) const noexcept;
|
||||
|
||||
void UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
|
||||
PageWatchMode mode = PageWatchMode::Write);
|
||||
void OnGpuMap(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>>
|
||||
ReserveBackingWrites(std::span<const RangeSet::Range> ranges);
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
enum class CpuFaultAction { Untracked, Continue, Download };
|
||||
|
||||
class TrackingSpinLock final {
|
||||
public:
|
||||
void lock() noexcept {
|
||||
@@ -85,18 +83,6 @@ public:
|
||||
KYTY_CLASS_NO_COPY(RegionManager);
|
||||
|
||||
[[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>
|
||||
[[nodiscard]] bool IsModified(uint64_t offset, uint64_t size) const {
|
||||
const auto [start, end] = GetPageRange(m_cpu_addr + offset, size);
|
||||
@@ -126,15 +112,15 @@ public:
|
||||
const auto [start, end] = GetPageRange(vaddr, size);
|
||||
if constexpr (source == DirtySource::Cpu && enable) {
|
||||
for (auto page = start; page < end; page++) {
|
||||
if (m_gpu_dirty.test(page) || m_fault_pending.test(page)) {
|
||||
EXIT("CPU dirty state conflicts with GPU dirty or pending fault state\n");
|
||||
if (m_gpu_dirty.test(page)) {
|
||||
EXIT("CPU dirty state conflicts with GPU dirty state\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (source == DirtySource::Gpu && enable) {
|
||||
for (auto page = start; page < end; page++) {
|
||||
if (m_cpu_dirty.test(page) || m_fault_pending.test(page)) {
|
||||
EXIT("GPU dirty state conflicts with CPU dirty or pending fault state\n");
|
||||
if (m_cpu_dirty.test(page)) {
|
||||
EXIT("GPU dirty state conflicts with CPU dirty state\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,110 +137,10 @@ public:
|
||||
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>
|
||||
RegionBits ForEachModifiedRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
||||
const auto [start, end] = GetPageRange(vaddr, size);
|
||||
auto mask = GetBits<source>();
|
||||
if constexpr (source == DirtySource::Cpu) {
|
||||
mask &= ~m_fault_pending;
|
||||
}
|
||||
for (auto page = 0u; page < start; page++) {
|
||||
mask.reset(page);
|
||||
}
|
||||
@@ -351,8 +237,6 @@ private:
|
||||
RegionBits m_cpu_dirty;
|
||||
RegionBits m_gpu_dirty;
|
||||
RegionBits m_writable;
|
||||
RegionBits m_fault_pending;
|
||||
RegionBits m_tracked;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
+2
-110
@@ -123,24 +123,6 @@ struct BufferCache::RetiredBuffer {
|
||||
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 {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
@@ -253,7 +235,7 @@ BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
ResourceMutex& resource_mutex)
|
||||
: m_graphics(graphics), m_scheduler(scheduler),
|
||||
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_stream_buffer(graphics, scheduler, MemoryUsage::Stream, 64 * MiB),
|
||||
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 32 * MiB),
|
||||
@@ -277,9 +259,6 @@ BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
}
|
||||
|
||||
BufferCache::~BufferCache() {
|
||||
if (m_fault_readback->Active()) {
|
||||
EXIT("BufferCache: destroyed with an active fault readback\n");
|
||||
}
|
||||
if (!m_gpu_modified_ranges.Empty()) {
|
||||
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) {
|
||||
if (vaddr == 0 || size == 0 || size > UINT64_MAX - vaddr) {
|
||||
EXIT("BufferCache: invalid unmap range\n");
|
||||
@@ -1201,7 +1093,7 @@ void BufferCache::RunGarbageCollector() {
|
||||
if (m_graphics.CanReportMemoryUsage()) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+3
-7
@@ -47,11 +47,9 @@ public:
|
||||
~BufferCache();
|
||||
KYTY_CLASS_NO_COPY(BufferCache);
|
||||
|
||||
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
|
||||
PageFaultPhase phase) noexcept;
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size);
|
||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||
void InvalidateMemory(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,
|
||||
bool is_written = false, bool is_read = true,
|
||||
bool is_formatted = false);
|
||||
@@ -90,7 +88,6 @@ private:
|
||||
struct DownloadCopy;
|
||||
struct DownloadRange;
|
||||
struct RetiredBuffer;
|
||||
struct FaultReadback;
|
||||
struct PendingBackingPublication;
|
||||
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
|
||||
[[nodiscard]] static uint64_t AlignDown(uint64_t value) noexcept;
|
||||
@@ -120,7 +117,6 @@ private:
|
||||
Common::Mutex m_mutex;
|
||||
std::shared_ptr<Buffer> m_null_buffer;
|
||||
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
||||
std::unique_ptr<FaultReadback> m_fault_readback;
|
||||
RangeSet m_gpu_modified_ranges;
|
||||
RangeSet m_image_invalidated_ranges;
|
||||
std::mutex m_publication_mutex;
|
||||
|
||||
+1
-23
@@ -7,33 +7,11 @@
|
||||
namespace Libs::Graphics {
|
||||
|
||||
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) {}
|
||||
|
||||
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 {
|
||||
constexpr uint64_t fault_size = 8;
|
||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||
|
||||
@@ -34,11 +34,6 @@ public:
|
||||
void RunGarbageCollector();
|
||||
|
||||
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;
|
||||
ResourceMutex m_resource_mutex;
|
||||
BufferCache m_buffer_cache;
|
||||
|
||||
@@ -1826,37 +1826,6 @@ bool TextureCache::TouchMeta(uint64_t address, uint32_t slice, bool is_clear) {
|
||||
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) {
|
||||
if (!GuestRange {address, size}.Valid()) {
|
||||
EXIT("TextureCache: invalid unmap range\n");
|
||||
|
||||
+3
-5
@@ -76,11 +76,9 @@ public:
|
||||
[[nodiscard]] bool ClearMeta(uint64_t address);
|
||||
[[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear);
|
||||
|
||||
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t address, uint64_t size,
|
||||
PageFaultPhase phase) noexcept;
|
||||
void UnmapMemory(uint64_t address, uint64_t size);
|
||||
void ProcessDownloadImages();
|
||||
void RunGarbageCollector();
|
||||
void UnmapMemory(uint64_t address, uint64_t size);
|
||||
void ProcessDownloadImages();
|
||||
void RunGarbageCollector();
|
||||
|
||||
private:
|
||||
enum class TransferDirection { Upload, Download };
|
||||
|
||||
+20
-24
@@ -18,6 +18,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
@@ -805,15 +806,15 @@ private:
|
||||
Common::Mutex m_mutex;
|
||||
};
|
||||
|
||||
static PhysicalMemory* g_physical_memory = nullptr;
|
||||
static FlexibleMemory* g_flexible_memory = nullptr;
|
||||
static PooledMemory* g_pooled_memory = nullptr;
|
||||
static VirtualRanges* g_virtual_ranges = nullptr;
|
||||
static GuestAddressSpace* g_guest_address_space = nullptr;
|
||||
static callback_func_t g_alloc_callback = nullptr;
|
||||
static callback_func_t g_free_callback = nullptr;
|
||||
static std::atomic<uint64_t> g_memory_pool_committed = 0;
|
||||
static void MemoryPoolSubtractCommitted(uint64_t len);
|
||||
static std::unique_ptr<PhysicalMemory> g_physical_memory;
|
||||
static std::unique_ptr<FlexibleMemory> g_flexible_memory;
|
||||
static std::unique_ptr<PooledMemory> g_pooled_memory;
|
||||
static std::unique_ptr<VirtualRanges> g_virtual_ranges;
|
||||
static std::unique_ptr<GuestAddressSpace> g_guest_address_space;
|
||||
static callback_func_t g_alloc_callback = nullptr;
|
||||
static callback_func_t g_free_callback = nullptr;
|
||||
static std::atomic<uint64_t> g_memory_pool_committed = 0;
|
||||
static void MemoryPoolSubtractCommitted(uint64_t len);
|
||||
// Keep host mappings, physical blocks, placeholders, and virtual ranges in step.
|
||||
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) {
|
||||
g_flexible_memory_size_frozen = true;
|
||||
VirtualMemory::Init();
|
||||
g_guest_address_space = new GuestAddressSpace(PhysicalMemory::TotalSize());
|
||||
g_physical_memory = new PhysicalMemory;
|
||||
g_flexible_memory = new FlexibleMemory;
|
||||
g_pooled_memory = new PooledMemory;
|
||||
g_virtual_ranges = new VirtualRanges;
|
||||
g_guest_address_space = std::make_unique<GuestAddressSpace>(PhysicalMemory::TotalSize());
|
||||
g_physical_memory = std::make_unique<PhysicalMemory>();
|
||||
g_flexible_memory = std::make_unique<FlexibleMemory>();
|
||||
g_pooled_memory = std::make_unique<PooledMemory>();
|
||||
g_virtual_ranges = std::make_unique<VirtualRanges>();
|
||||
EXIT_IF(!g_guest_address_space->SelfTest());
|
||||
EXIT_IF(!SelfTestSub64SharedPlaceholderAlias());
|
||||
}
|
||||
@@ -982,16 +983,11 @@ KYTY_SUBSYSTEM_INIT(Memory) {
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Memory) {
|
||||
delete g_pooled_memory;
|
||||
g_pooled_memory = nullptr;
|
||||
delete g_flexible_memory;
|
||||
g_flexible_memory = nullptr;
|
||||
delete g_physical_memory;
|
||||
g_physical_memory = nullptr;
|
||||
delete g_virtual_ranges;
|
||||
g_virtual_ranges = nullptr;
|
||||
delete g_guest_address_space;
|
||||
g_guest_address_space = nullptr;
|
||||
g_pooled_memory.reset();
|
||||
g_flexible_memory.reset();
|
||||
g_physical_memory.reset();
|
||||
g_virtual_ranges.reset();
|
||||
g_guest_address_space.reset();
|
||||
}
|
||||
|
||||
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",
|
||||
partial_unmap_image_id &&
|
||||
!texture_cache.FindImageFromRange(partial_unmap_image.info.data.address,
|
||||
0x2000, false) &&
|
||||
!texture_cache.InvalidateMemory(
|
||||
PageFaultAccess::Write, partial_unmap_image.info.data.address + 0x1000,
|
||||
0x1000, PageFaultPhase::Invalidate),
|
||||
0x2000, false),
|
||||
"partial unmap left the deleted image's mapped tail tracked");
|
||||
|
||||
constexpr uint64_t unformatted_alias_offset = 0x2500000;
|
||||
@@ -14459,17 +14456,6 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
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() {
|
||||
(void)TextureGetRenderTargetFormat(12u, 7u, 3u);
|
||||
std::_Exit(0x7f);
|
||||
@@ -16110,7 +16096,7 @@ void CheckStorageTextureGpuOwnedRebindState() {
|
||||
auto* memory = reinterpret_cast<uint8_t*>(guest_memory);
|
||||
Require("StorageTextureGpuOwnedRebind", "allocation", guest_memory == base,
|
||||
"fixed guest-owner allocation failed");
|
||||
PageManager page_manager(CacheFault, nullptr);
|
||||
PageManager page_manager;
|
||||
MemoryTracker tracker(page_manager);
|
||||
page_manager.OnGpuMap(base, size);
|
||||
tracker.ForEachUploadRange(
|
||||
|
||||
Reference in New Issue
Block a user