From e29ced29fa54bf36a06545c53d1d523f3a31f883 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 23 Apr 2023 21:55:16 +0200 Subject: QueryCache: rework async downloads. --- src/video_core/query_cache.h | 122 +++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 28 deletions(-) (limited to 'src/video_core/query_cache.h') diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index cd339b99d..2a14cc36a 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -17,13 +17,19 @@ #include "common/assert.h" #include "common/settings.h" +#include "core/memory.h" #include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" +#include "video_core/texture_cache/slot_vector.h" namespace VideoCommon { +using AsyncJobId = SlotId; + +static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; + template class CounterStreamBase { public: @@ -93,9 +99,13 @@ private: template class QueryCacheBase : public VideoCommon::ChannelSetupCaches { public: - explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_) - : rasterizer{rasterizer_}, streams{{CounterStream{static_cast(*this), - VideoCore::QueryType::SamplesPassed}}} {} + explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, + Core::Memory::Memory& cpu_memory_) + : rasterizer{rasterizer_}, cpu_memory{cpu_memory_}, streams{ + {CounterStream{static_cast(*this), + VideoCore::QueryType::SamplesPassed}}} { + (void) slot_async_jobs.insert(); // Null value + } void InvalidateRegion(VAddr addr, std::size_t size) { std::unique_lock lock{mutex}; @@ -126,10 +136,15 @@ public: query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); } - query->BindCounter(Stream(type).Current(), timestamp); - if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { - AsyncFlushQuery(*cpu_addr); + auto result = query->BindCounter(Stream(type).Current()); + if (result) { + auto async_job_id = query->GetAsyncJob(); + auto& async_job = slot_async_jobs[async_job_id]; + async_job.collected = true; + async_job.value = *result; + query->SetAsyncJob(NULL_ASYNC_JOB_ID); } + AsyncFlushQuery(query, timestamp, lock); } /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. @@ -201,15 +216,25 @@ public: committed_flushes.pop_front(); return; } - for (VAddr query_address : *flush_list) { - FlushAndRemoveRegion(query_address, 4); + for (AsyncJobId async_job_id : *flush_list) { + AsyncJob& async_job = slot_async_jobs[async_job_id]; + if (!async_job.collected) { + FlushAndRemoveRegion(async_job.query_location, 2, true); + } } committed_flushes.pop_front(); } private: + struct AsyncJob { + bool collected = false; + u64 value = 0; + VAddr query_location = 0; + std::optional timestamp{}; + }; + /// Flushes a memory range to guest memory and removes it from the cache. - void FlushAndRemoveRegion(VAddr addr, std::size_t size) { + void FlushAndRemoveRegion(VAddr addr, std::size_t size, bool async = false) { const u64 addr_begin = addr; const u64 addr_end = addr_begin + size; const auto in_range = [addr_begin, addr_end](const CachedQuery& query) { @@ -230,7 +255,16 @@ private: continue; } rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1); - query.Flush(); + AsyncJobId async_job_id = query.GetAsyncJob(); + auto flush_result = query.Flush(async); + if (async_job_id == NULL_ASYNC_JOB_ID) { + ASSERT_MSG(false, "This should not be reachable at all"); + continue; + } + AsyncJob& async_job = slot_async_jobs[async_job_id]; + async_job.collected = true; + async_job.value = flush_result; + query.SetAsyncJob(NULL_ASYNC_JOB_ID); } std::erase_if(contents, in_range); } @@ -257,17 +291,43 @@ private: return found != std::end(contents) ? &*found : nullptr; } - void AsyncFlushQuery(VAddr addr) { + void AsyncFlushQuery(CachedQuery* query, std::optional timestamp, + std::unique_lock& lock) { + const AsyncJobId new_async_job_id = slot_async_jobs.insert(); + AsyncJob& async_job = slot_async_jobs[new_async_job_id]; + query->SetAsyncJob(new_async_job_id); + async_job.query_location = query->GetCpuAddr(); + async_job.collected = false; + if (!uncommitted_flushes) { - uncommitted_flushes = std::make_shared>(); + uncommitted_flushes = std::make_shared>(); } - uncommitted_flushes->push_back(addr); + uncommitted_flushes->push_back(new_async_job_id); + lock.unlock(); + std::function operation([this, new_async_job_id, timestamp] { + std::unique_lock local_lock{mutex}; + AsyncJob& async_job = slot_async_jobs[new_async_job_id]; + if (timestamp) { + u64 timestamp_value = *timestamp; + cpu_memory.WriteBlockUnsafe(async_job.query_location + sizeof(u64), + ×tamp_value, sizeof(8)); + cpu_memory.WriteBlockUnsafe(async_job.query_location, &async_job.value, sizeof(8)); + } else { + u32 small_value = static_cast(async_job.value); + cpu_memory.WriteBlockUnsafe(async_job.query_location, &small_value, sizeof(u32)); + } + slot_async_jobs.erase(new_async_job_id); + }); + rasterizer.SyncOperation(std::move(operation)); } static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; static constexpr unsigned YUZU_PAGEBITS = 12; + SlotVector slot_async_jobs; + VideoCore::RasterizerInterface& rasterizer; + Core::Memory::Memory& cpu_memory; mutable std::recursive_mutex mutex; @@ -275,8 +335,8 @@ private: std::array streams; - std::shared_ptr> uncommitted_flushes{}; - std::list>> committed_flushes; + std::shared_ptr> uncommitted_flushes{}; + std::list>> committed_flushes; }; template @@ -295,12 +355,12 @@ public: virtual ~HostCounterBase() = default; /// Returns the current value of the query. - u64 Query() { + u64 Query(bool async = false) { if (result) { return *result; } - u64 value = BlockingQuery() + base_result; + u64 value = BlockingQuery(async) + base_result; if (dependency) { value += dependency->Query(); dependency = nullptr; @@ -321,7 +381,7 @@ public: protected: /// Returns the value of query from the backend API blocking as needed. - virtual u64 BlockingQuery() const = 0; + virtual u64 BlockingQuery(bool async = false) const = 0; private: std::shared_ptr dependency; ///< Counter to add to this value. @@ -344,26 +404,23 @@ public: CachedQueryBase& operator=(const CachedQueryBase&) = delete; /// Flushes the query to guest memory. - virtual void Flush() { + virtual u64 Flush(bool async = false) { // When counter is nullptr it means that it's just been reset. We are supposed to write a // zero in these cases. - const u64 value = counter ? counter->Query() : 0; - std::memcpy(host_ptr, &value, sizeof(u64)); - - if (timestamp) { - std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64)); - } + const u64 value = counter ? counter->Query(async) : 0; + return value; } /// Binds a counter to this query. - void BindCounter(std::shared_ptr counter_, std::optional timestamp_) { + std::optional BindCounter(std::shared_ptr counter_) { + std::optional result{}; if (counter) { // If there's an old counter set it means the query is being rewritten by the game. // To avoid losing the data forever, flush here. - Flush(); + result = std::make_optional(Flush()); } counter = std::move(counter_); - timestamp = timestamp_; + return result; } VAddr GetCpuAddr() const noexcept { @@ -378,6 +435,14 @@ public: return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE; } + void SetAsyncJob(AsyncJobId assigned_async_job_) { + assigned_async_job = assigned_async_job_; + } + + AsyncJobId GetAsyncJob() const { + return assigned_async_job; + } + protected: /// Returns true when querying the counter may potentially block. bool WaitPending() const noexcept { @@ -393,6 +458,7 @@ private: u8* host_ptr; ///< Writable host pointer. std::shared_ptr counter; ///< Host counter to query, owns the dependency tree. std::optional timestamp; ///< Timestamp to flush to guest memory. + AsyncJobId assigned_async_job; }; } // namespace VideoCommon -- cgit v1.2.3