2022-04-23 08:59:50 +00:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2020-02-18 00:19:26 +00:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <algorithm>
|
2021-11-05 14:52:31 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <memory>
|
2020-02-18 21:20:39 +00:00
|
|
|
#include <queue>
|
2020-02-18 00:19:26 +00:00
|
|
|
|
|
|
|
#include "common/common_types.h"
|
2020-12-30 05:25:23 +00:00
|
|
|
#include "video_core/delayed_destruction_ring.h"
|
2020-02-18 00:19:26 +00:00
|
|
|
#include "video_core/gpu.h"
|
|
|
|
#include "video_core/rasterizer_interface.h"
|
|
|
|
|
|
|
|
namespace VideoCommon {
|
|
|
|
|
|
|
|
class FenceBase {
|
|
|
|
public:
|
2020-12-04 19:39:12 +00:00
|
|
|
explicit FenceBase(u32 payload_, bool is_stubbed_)
|
|
|
|
: address{}, payload{payload_}, is_semaphore{false}, is_stubbed{is_stubbed_} {}
|
2020-02-19 17:40:37 +00:00
|
|
|
|
2021-11-05 14:52:31 +00:00
|
|
|
explicit FenceBase(u8* address_, u32 payload_, bool is_stubbed_)
|
2020-12-04 19:39:12 +00:00
|
|
|
: address{address_}, payload{payload_}, is_semaphore{true}, is_stubbed{is_stubbed_} {}
|
2020-02-18 00:19:26 +00:00
|
|
|
|
2021-11-05 14:52:31 +00:00
|
|
|
u8* GetAddress() const {
|
2020-02-18 00:19:26 +00:00
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2020-04-16 16:29:53 +00:00
|
|
|
u32 GetPayload() const {
|
2020-02-18 00:19:26 +00:00
|
|
|
return payload;
|
|
|
|
}
|
|
|
|
|
2020-04-16 16:29:53 +00:00
|
|
|
bool IsSemaphore() const {
|
2020-02-19 17:40:37 +00:00
|
|
|
return is_semaphore;
|
|
|
|
}
|
|
|
|
|
2020-02-18 00:19:26 +00:00
|
|
|
private:
|
2021-11-05 14:52:31 +00:00
|
|
|
u8* address;
|
2020-02-18 00:19:26 +00:00
|
|
|
u32 payload;
|
2020-02-19 17:40:37 +00:00
|
|
|
bool is_semaphore;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
bool is_stubbed;
|
2020-02-18 00:19:26 +00:00
|
|
|
};
|
|
|
|
|
2020-04-15 20:36:14 +00:00
|
|
|
template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache>
|
2020-02-18 00:19:26 +00:00
|
|
|
class FenceManager {
|
|
|
|
public:
|
2020-12-30 05:25:23 +00:00
|
|
|
/// Notify the fence manager about a new frame
|
|
|
|
void TickFrame() {
|
|
|
|
delayed_destruction_ring.Tick();
|
|
|
|
}
|
|
|
|
|
2021-07-07 14:42:26 +00:00
|
|
|
// Unlike other fences, this one doesn't
|
|
|
|
void SignalOrdering() {
|
|
|
|
std::scoped_lock lock{buffer_cache.mutex};
|
|
|
|
buffer_cache.AccumulateFlushes();
|
|
|
|
}
|
|
|
|
|
2021-11-05 14:52:31 +00:00
|
|
|
void SignalSemaphore(u8* addr, u32 value) {
|
2020-02-18 00:19:26 +00:00
|
|
|
TryReleasePendingFences();
|
2020-04-22 15:14:40 +00:00
|
|
|
const bool should_flush = ShouldFlush();
|
2020-04-16 16:29:53 +00:00
|
|
|
CommitAsyncFlushes();
|
2020-02-19 17:40:37 +00:00
|
|
|
TFence new_fence = CreateFence(addr, value, !should_flush);
|
2020-02-19 14:49:07 +00:00
|
|
|
fences.push(new_fence);
|
|
|
|
QueueFence(new_fence);
|
2020-02-19 17:40:37 +00:00
|
|
|
if (should_flush) {
|
|
|
|
rasterizer.FlushCommands();
|
|
|
|
}
|
|
|
|
rasterizer.SyncGuestHost();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SignalSyncPoint(u32 value) {
|
|
|
|
TryReleasePendingFences();
|
2020-04-22 15:14:40 +00:00
|
|
|
const bool should_flush = ShouldFlush();
|
2020-04-16 16:29:53 +00:00
|
|
|
CommitAsyncFlushes();
|
2020-02-19 17:40:37 +00:00
|
|
|
TFence new_fence = CreateFence(value, !should_flush);
|
|
|
|
fences.push(new_fence);
|
|
|
|
QueueFence(new_fence);
|
|
|
|
if (should_flush) {
|
|
|
|
rasterizer.FlushCommands();
|
|
|
|
}
|
2020-02-18 00:19:26 +00:00
|
|
|
rasterizer.SyncGuestHost();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaitPendingFences() {
|
|
|
|
while (!fences.empty()) {
|
|
|
|
TFence& current_fence = fences.front();
|
2020-04-16 16:29:53 +00:00
|
|
|
if (ShouldWait()) {
|
2020-02-18 02:15:43 +00:00
|
|
|
WaitFence(current_fence);
|
|
|
|
}
|
2020-04-16 16:29:53 +00:00
|
|
|
PopAsyncFlushes();
|
2020-02-19 17:40:37 +00:00
|
|
|
if (current_fence->IsSemaphore()) {
|
2021-11-05 14:52:31 +00:00
|
|
|
char* address = reinterpret_cast<char*>(current_fence->GetAddress());
|
|
|
|
auto payload = current_fence->GetPayload();
|
|
|
|
std::memcpy(address, &payload, sizeof(payload));
|
2020-02-19 17:40:37 +00:00
|
|
|
} else {
|
|
|
|
gpu.IncrementSyncPoint(current_fence->GetPayload());
|
|
|
|
}
|
2020-12-30 05:25:23 +00:00
|
|
|
PopFence();
|
2020-02-18 00:19:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2020-06-12 00:24:45 +00:00
|
|
|
explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
|
|
|
|
TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
|
|
|
|
TQueryCache& query_cache_)
|
2021-11-05 14:52:31 +00:00
|
|
|
: rasterizer{rasterizer_}, gpu{gpu_}, texture_cache{texture_cache_},
|
|
|
|
buffer_cache{buffer_cache_}, query_cache{query_cache_} {}
|
2020-02-18 00:19:26 +00:00
|
|
|
|
2020-06-12 00:24:45 +00:00
|
|
|
virtual ~FenceManager() = default;
|
2020-04-16 16:29:53 +00:00
|
|
|
|
|
|
|
/// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is
|
|
|
|
/// true
|
2020-02-19 17:40:37 +00:00
|
|
|
virtual TFence CreateFence(u32 value, bool is_stubbed) = 0;
|
2020-04-16 16:29:53 +00:00
|
|
|
/// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true
|
2021-11-05 14:52:31 +00:00
|
|
|
virtual TFence CreateFence(u8* addr, u32 value, bool is_stubbed) = 0;
|
2020-04-16 16:29:53 +00:00
|
|
|
/// Queues a fence into the backend if the fence isn't stubbed.
|
2020-02-18 00:19:26 +00:00
|
|
|
virtual void QueueFence(TFence& fence) = 0;
|
2020-04-16 16:29:53 +00:00
|
|
|
/// Notifies that the backend fence has been signaled/reached in host GPU.
|
|
|
|
virtual bool IsFenceSignaled(TFence& fence) const = 0;
|
|
|
|
/// Waits until a fence has been signalled by the host GPU.
|
2020-02-18 00:19:26 +00:00
|
|
|
virtual void WaitFence(TFence& fence) = 0;
|
|
|
|
|
|
|
|
VideoCore::RasterizerInterface& rasterizer;
|
2020-06-12 00:24:45 +00:00
|
|
|
Tegra::GPU& gpu;
|
2020-02-18 00:19:26 +00:00
|
|
|
TTextureCache& texture_cache;
|
2020-02-18 21:20:39 +00:00
|
|
|
TTBufferCache& buffer_cache;
|
2020-04-15 20:36:14 +00:00
|
|
|
TQueryCache& query_cache;
|
2020-02-18 00:19:26 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
void TryReleasePendingFences() {
|
|
|
|
while (!fences.empty()) {
|
|
|
|
TFence& current_fence = fences.front();
|
2020-04-16 16:29:53 +00:00
|
|
|
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
|
2020-02-18 00:19:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-04-16 16:29:53 +00:00
|
|
|
PopAsyncFlushes();
|
2020-02-19 17:40:37 +00:00
|
|
|
if (current_fence->IsSemaphore()) {
|
2021-11-05 14:52:31 +00:00
|
|
|
char* address = reinterpret_cast<char*>(current_fence->GetAddress());
|
|
|
|
const auto payload = current_fence->GetPayload();
|
|
|
|
std::memcpy(address, &payload, sizeof(payload));
|
2020-02-19 17:40:37 +00:00
|
|
|
} else {
|
|
|
|
gpu.IncrementSyncPoint(current_fence->GetPayload());
|
|
|
|
}
|
2020-12-30 05:25:23 +00:00
|
|
|
PopFence();
|
2020-02-18 00:19:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 16:29:53 +00:00
|
|
|
bool ShouldWait() const {
|
2021-01-16 23:48:58 +00:00
|
|
|
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
2020-04-16 16:29:53 +00:00
|
|
|
return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
|
|
|
|
query_cache.ShouldWaitAsyncFlushes();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ShouldFlush() const {
|
2021-01-16 23:48:58 +00:00
|
|
|
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
2020-04-16 16:29:53 +00:00
|
|
|
return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() ||
|
|
|
|
query_cache.HasUncommittedFlushes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopAsyncFlushes() {
|
2021-01-16 23:48:58 +00:00
|
|
|
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
2020-04-16 16:29:53 +00:00
|
|
|
texture_cache.PopAsyncFlushes();
|
|
|
|
buffer_cache.PopAsyncFlushes();
|
|
|
|
query_cache.PopAsyncFlushes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommitAsyncFlushes() {
|
2021-01-16 23:48:58 +00:00
|
|
|
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
2020-04-16 16:29:53 +00:00
|
|
|
texture_cache.CommitAsyncFlushes();
|
|
|
|
buffer_cache.CommitAsyncFlushes();
|
|
|
|
query_cache.CommitAsyncFlushes();
|
|
|
|
}
|
|
|
|
|
2020-12-30 05:25:23 +00:00
|
|
|
void PopFence() {
|
|
|
|
delayed_destruction_ring.Push(std::move(fences.front()));
|
|
|
|
fences.pop();
|
|
|
|
}
|
|
|
|
|
2020-02-18 00:19:26 +00:00
|
|
|
std::queue<TFence> fences;
|
2020-12-30 05:25:23 +00:00
|
|
|
|
|
|
|
DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
|
2020-02-18 00:19:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace VideoCommon
|