gpu: skip drains for CPU-only memory unmaps

This commit is contained in:
nmzik
2026-10-05 21:00:12 +04:00
parent 5b826c6e4a
commit e6fce45d3a
6 changed files with 81 additions and 37 deletions
+5
View File
@@ -1962,6 +1962,11 @@ bool TextureCache::TouchMeta(uint64_t address, uint32_t slice, bool is_clear) {
return true;
}
bool TextureCache::IsRegionRegistered(uint64_t address, uint64_t size) {
std::scoped_lock lock {m_lock};
return !FindImagesInRegion(address, size, false).empty();
}
void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
if (!GuestRange {address, size}.Valid()) {
EXIT("TextureCache: invalid unmap range\n");
+1
View File
@@ -61,6 +61,7 @@ public:
uint32_t packed_clear);
void InvalidateMemory(uint64_t address, uint64_t size);
void InvalidateMemoryFromGPU(uint64_t address, uint64_t size);
[[nodiscard]] bool IsRegionRegistered(uint64_t address, uint64_t size);
[[nodiscard]] bool IsMeta(uint64_t address);
[[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice);
@@ -226,43 +226,37 @@ void CommandScheduler::PopPendingOperations() {
}
void CommandScheduler::DeferOperation(Common::UniqueFunction<void>&& operation) {
QueueOperation(std::move(operation), false);
}
void CommandScheduler::DeferPriorityOperation(Common::UniqueFunction<void>&& operation) {
QueueOperation(std::move(operation), true);
}
void CommandScheduler::QueueOperation(Common::UniqueFunction<void>&& operation, bool priority) {
CheckActive();
EXIT_IF(!operation);
std::unique_lock lock(m_operation_mutex);
if (m_operation_state == OperationState::Open) {
m_pending_operations.push({std::move(operation), CurrentTick()});
return;
}
if (g_deferred_callback_scheduler == this) {
auto& queue = priority ? m_priority_operations : m_pending_operations;
queue.push({std::move(operation), CurrentTick()});
lock.unlock();
operation();
if (priority) {
m_operation_available.notify_one();
}
return;
}
m_operation_available.wait(lock,
[this] { return m_operation_state == OperationState::Closed; });
if (g_deferred_callback_scheduler != this) {
m_operation_available.wait(lock,
[this] { return m_operation_state == OperationState::Closed; });
}
lock.unlock();
operation();
}
void CommandScheduler::DeferPriorityOperation(Common::UniqueFunction<void>&& operation) {
CheckActive();
EXIT_IF(!operation);
std::unique_lock lock(m_operation_mutex);
if (m_operation_state == OperationState::Open) {
m_priority_operations.push({std::move(operation), CurrentTick()});
lock.unlock();
m_operation_available.notify_one();
return;
}
if (g_deferred_callback_scheduler == this) {
lock.unlock();
operation();
return;
}
m_operation_available.wait(lock,
[this] { return m_operation_state == OperationState::Closed; });
lock.unlock();
operation();
bool CommandScheduler::HasPendingPriorityOperations() {
std::lock_guard lock(m_operation_mutex);
return !m_priority_operations.empty() || m_priority_active;
}
void CommandScheduler::PriorityOperationsThread(std::stop_token stop) {
@@ -38,8 +38,10 @@ public:
void PopPendingOperations();
void DrainPriorityOperations();
void WaitPriorityOperations(uint64_t tick);
// Guest-memory completions use the priority queue; normal callbacks maintain GPU resources.
void DeferOperation(Common::UniqueFunction<void>&& operation);
void DeferPriorityOperation(Common::UniqueFunction<void>&& operation);
[[nodiscard]] bool HasPendingPriorityOperations();
[[nodiscard]] static bool InDeferredOperation() noexcept;
[[nodiscard]] bool Active() const noexcept { return m_command.m_registers != nullptr; }
@@ -82,6 +84,7 @@ private:
void BeginNext();
void PriorityOperationsThread(std::stop_token stop);
void QueueOperation(Common::UniqueFunction<void>&& operation, bool priority);
void RunOperation(Common::UniqueFunction<void>&& operation);
MasterSemaphore m_master;
@@ -99,7 +99,12 @@ void RenderContext::UnmapMemory(uint64_t vaddr, uint64_t size) {
vaddr, size);
}
const auto unmap = [this, vaddr, size] {
if (m_command_scheduler.Active()) {
// Check cache ownership on the GPU thread. Guest-memory callbacks can still
// access a range with no cached data, so they must finish before it is unmapped.
if (m_command_scheduler.Active() &&
(m_buffer_cache.IsRegionRegistered(vaddr, size) ||
m_texture_cache.IsRegionRegistered(vaddr, size) ||
m_command_scheduler.HasPendingPriorityOperations())) {
const auto tick = m_command_scheduler.CurrentTick();
m_command_scheduler.Finish();
m_command_scheduler.WaitPriorityOperations(tick);