mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
perf: sync only GPU-dirty readbacks
This commit is contained in:
+37
-5
@@ -3,15 +3,18 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/guest_gpu/graphicsRun.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -288,10 +291,29 @@ void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
return;
|
||||
}
|
||||
m_memory_tracker.InvalidateRegion(vaddr, size,
|
||||
[this, vaddr, size] { ReadMemory(vaddr, size); });
|
||||
[this, vaddr, size] { ReadMemory(vaddr, size, true); });
|
||||
}
|
||||
|
||||
void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
||||
void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) {
|
||||
if (Gpu::IsGpuThread()) {
|
||||
ReadMemoryOnGpu(vaddr, size, is_write);
|
||||
return;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported buffer readback from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
if (m_resource_mutex.IsOwnedByCurrentThread()) {
|
||||
EXIT("unsupported buffer readback from a pre-owned resource transaction, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
m_scheduler.Context().GetGpu().SendCommandSync(
|
||||
[this, vaddr, size, is_write] { ReadMemoryOnGpu(vaddr, size, is_write); });
|
||||
}
|
||||
|
||||
void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write) {
|
||||
std::vector<DownloadCopy> copies;
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
@@ -322,9 +344,16 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (copies.empty()) {
|
||||
return;
|
||||
if (copies.empty()) {
|
||||
if (!is_write) {
|
||||
return;
|
||||
}
|
||||
// A preceding read fault can consume the last GPU-owned copy after this write
|
||||
// invalidation has already chosen to flush. Complete the CPU ownership handoff even
|
||||
// though this callback no longer has bytes to download.
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto downloads = RecordDownloads(copies);
|
||||
m_scheduler.FinishCurrent();
|
||||
@@ -336,6 +365,9 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
// The enumeration above covered whole dirty pages and every exact interval on them.
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
|
||||
if (is_write) {
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -46,7 +46,7 @@ public:
|
||||
KYTY_CLASS_NO_COPY(BufferCache);
|
||||
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size, bool is_write = false);
|
||||
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,
|
||||
@@ -97,6 +97,7 @@ private:
|
||||
void PublishDownloads(std::span<const DownloadRange> downloads);
|
||||
void QueueGarbageDownload(std::span<const DownloadCopy> copies, RetiredBuffer retire);
|
||||
void WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data);
|
||||
void ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
|
||||
+8
-57
@@ -1,7 +1,6 @@
|
||||
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/command_processor/commandProcessor.h"
|
||||
#include "graphics/guest_gpu/graphicsRun.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
namespace Libs::Graphics {
|
||||
@@ -18,69 +17,21 @@ bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vadd
|
||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||
return false;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported guest-memory fault from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " access=%u\n",
|
||||
fault_vaddr, static_cast<uint32_t>(access));
|
||||
if (access == PageFaultAccess::Write) {
|
||||
m_buffer_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
m_texture_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
} else {
|
||||
m_buffer_cache.ReadMemory(fault_vaddr, fault_size);
|
||||
}
|
||||
bool handled = false;
|
||||
const auto resolve = [this, access, fault_vaddr, &handled](CommandProcessor& cp) {
|
||||
cp.BeginReadbackTransaction();
|
||||
{
|
||||
ResourceMutex::FaultScope fault(m_resource_mutex);
|
||||
if (access == PageFaultAccess::Write) {
|
||||
m_buffer_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
m_texture_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
} else {
|
||||
m_buffer_cache.ReadMemory(fault_vaddr, fault_size);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
cp.EndReadbackTransaction();
|
||||
};
|
||||
if (auto* cp = Gpu::CurrentCommandProcessor(); cp != nullptr) {
|
||||
resolve(*cp);
|
||||
return handled;
|
||||
}
|
||||
if (m_resource_mutex.IsOwnedByCurrentThread()) {
|
||||
EXIT("unsupported page fault from a pre-owned resource transaction, addr=0x%016" PRIx64
|
||||
" access=%u\n",
|
||||
fault_vaddr, static_cast<uint32_t>(access));
|
||||
}
|
||||
EXIT_IF(m_gpu == nullptr);
|
||||
m_gpu->SendCommandSyncWithProcessor(resolve);
|
||||
return handled;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (!IsMapped(vaddr, size)) {
|
||||
return false;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported memory invalidation from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
const auto resolve = [this, vaddr, size](CommandProcessor& cp) {
|
||||
cp.BeginReadbackTransaction();
|
||||
{
|
||||
ResourceMutex::FaultScope fault(m_resource_mutex);
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
}
|
||||
cp.EndReadbackTransaction();
|
||||
};
|
||||
if (auto* cp = Gpu::CurrentCommandProcessor(); cp != nullptr) {
|
||||
resolve(*cp);
|
||||
return true;
|
||||
}
|
||||
if (m_resource_mutex.IsOwnedByCurrentThread()) {
|
||||
EXIT("unsupported memory invalidation from a pre-owned resource transaction, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
EXIT_IF(m_gpu == nullptr);
|
||||
m_gpu->SendCommandSyncWithProcessor(resolve);
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+2
-48
@@ -4,18 +4,10 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
ResourceMutex::FaultScope::FaultScope(ResourceMutex& mutex): m_mutex(mutex) {
|
||||
m_resource_preowned = m_mutex.BeginFault();
|
||||
}
|
||||
|
||||
ResourceMutex::FaultScope::~FaultScope() {
|
||||
m_mutex.EndFault(m_resource_preowned);
|
||||
}
|
||||
|
||||
ResourceMutex::~ResourceMutex() {
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner != std::thread::id {} || m_fault_owner != std::thread::id {}) {
|
||||
EXIT("ResourceMutex destroyed with an active owner or fault transaction\n");
|
||||
if (m_resource_owner != std::thread::id {}) {
|
||||
EXIT("ResourceMutex destroyed with an active owner\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +18,6 @@ void ResourceMutex::lock() {
|
||||
if (m_resource_owner == current) {
|
||||
EXIT("recursive resource transaction\n");
|
||||
}
|
||||
if (m_fault_owner == current) {
|
||||
EXIT("resource transaction re-entered from a page-fault callback\n");
|
||||
}
|
||||
}
|
||||
m_resource.lock();
|
||||
std::lock_guard state(m_state);
|
||||
@@ -55,39 +44,4 @@ bool ResourceMutex::IsOwnedByCurrentThread() {
|
||||
return m_resource_owner == std::this_thread::get_id();
|
||||
}
|
||||
|
||||
bool ResourceMutex::BeginFault() {
|
||||
const auto current = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard state(m_state);
|
||||
if (m_fault_owner == current) {
|
||||
EXIT("nested resource page-fault transaction\n");
|
||||
}
|
||||
if (m_resource_owner == current) {
|
||||
m_fault_owner = current;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
lock();
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner != current || m_fault_owner != std::thread::id {}) {
|
||||
EXIT("resource fault transaction acquired inconsistent ownership\n");
|
||||
}
|
||||
m_fault_owner = current;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceMutex::EndFault(bool resource_preowned) {
|
||||
const auto current = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard state(m_state);
|
||||
if (m_fault_owner != current || m_resource_owner != current) {
|
||||
EXIT("resource page-fault transaction ended with inconsistent ownership\n");
|
||||
}
|
||||
m_fault_owner = {};
|
||||
}
|
||||
if (!resource_preowned) {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
+1
-19
@@ -8,21 +8,9 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
// Owner-tracked shared buffer/image transaction. External faults pause GPU submissions first;
|
||||
// command-processor faults drain pending guest processors before entering this transaction.
|
||||
// Owner-tracked shared buffer/image transaction.
|
||||
class ResourceMutex final {
|
||||
public:
|
||||
class FaultScope final {
|
||||
public:
|
||||
explicit FaultScope(ResourceMutex& mutex);
|
||||
~FaultScope();
|
||||
KYTY_CLASS_NO_COPY(FaultScope);
|
||||
|
||||
private:
|
||||
ResourceMutex& m_mutex;
|
||||
bool m_resource_preowned = false;
|
||||
};
|
||||
|
||||
ResourceMutex() = default;
|
||||
~ResourceMutex();
|
||||
KYTY_CLASS_NO_COPY(ResourceMutex);
|
||||
@@ -32,15 +20,9 @@ public:
|
||||
[[nodiscard]] bool IsOwnedByCurrentThread();
|
||||
|
||||
private:
|
||||
friend class FaultScope;
|
||||
|
||||
[[nodiscard]] bool BeginFault();
|
||||
void EndFault(bool resource_preowned);
|
||||
|
||||
std::mutex m_resource;
|
||||
std::mutex m_state;
|
||||
std::thread::id m_resource_owner;
|
||||
std::thread::id m_fault_owner;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
Reference in New Issue
Block a user