mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #3677 from FernandoS27/better-sync
Introduce Predictive Flushing and Improve ASYNC GPUmerge-requests/60/head
commit
bf2ddb8fd5
@ -0,0 +1,170 @@
|
|||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/memory_manager.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
class FenceBase {
|
||||||
|
public:
|
||||||
|
FenceBase(u32 payload, bool is_stubbed)
|
||||||
|
: address{}, payload{payload}, is_semaphore{false}, is_stubbed{is_stubbed} {}
|
||||||
|
|
||||||
|
FenceBase(GPUVAddr address, u32 payload, bool is_stubbed)
|
||||||
|
: address{address}, payload{payload}, is_semaphore{true}, is_stubbed{is_stubbed} {}
|
||||||
|
|
||||||
|
GPUVAddr GetAddress() const {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetPayload() const {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSemaphore() const {
|
||||||
|
return is_semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GPUVAddr address;
|
||||||
|
u32 payload;
|
||||||
|
bool is_semaphore;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool is_stubbed;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache>
|
||||||
|
class FenceManager {
|
||||||
|
public:
|
||||||
|
void SignalSemaphore(GPUVAddr addr, u32 value) {
|
||||||
|
TryReleasePendingFences();
|
||||||
|
const bool should_flush = ShouldFlush();
|
||||||
|
CommitAsyncFlushes();
|
||||||
|
TFence new_fence = CreateFence(addr, value, !should_flush);
|
||||||
|
fences.push(new_fence);
|
||||||
|
QueueFence(new_fence);
|
||||||
|
if (should_flush) {
|
||||||
|
rasterizer.FlushCommands();
|
||||||
|
}
|
||||||
|
rasterizer.SyncGuestHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalSyncPoint(u32 value) {
|
||||||
|
TryReleasePendingFences();
|
||||||
|
const bool should_flush = ShouldFlush();
|
||||||
|
CommitAsyncFlushes();
|
||||||
|
TFence new_fence = CreateFence(value, !should_flush);
|
||||||
|
fences.push(new_fence);
|
||||||
|
QueueFence(new_fence);
|
||||||
|
if (should_flush) {
|
||||||
|
rasterizer.FlushCommands();
|
||||||
|
}
|
||||||
|
rasterizer.SyncGuestHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitPendingFences() {
|
||||||
|
auto& gpu{system.GPU()};
|
||||||
|
auto& memory_manager{gpu.MemoryManager()};
|
||||||
|
while (!fences.empty()) {
|
||||||
|
TFence& current_fence = fences.front();
|
||||||
|
if (ShouldWait()) {
|
||||||
|
WaitFence(current_fence);
|
||||||
|
}
|
||||||
|
PopAsyncFlushes();
|
||||||
|
if (current_fence->IsSemaphore()) {
|
||||||
|
memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
|
||||||
|
} else {
|
||||||
|
gpu.IncrementSyncPoint(current_fence->GetPayload());
|
||||||
|
}
|
||||||
|
fences.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||||
|
TTextureCache& texture_cache, TTBufferCache& buffer_cache,
|
||||||
|
TQueryCache& query_cache)
|
||||||
|
: system{system}, rasterizer{rasterizer}, texture_cache{texture_cache},
|
||||||
|
buffer_cache{buffer_cache}, query_cache{query_cache} {}
|
||||||
|
|
||||||
|
virtual ~FenceManager() {}
|
||||||
|
|
||||||
|
/// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is
|
||||||
|
/// true
|
||||||
|
virtual TFence CreateFence(u32 value, bool is_stubbed) = 0;
|
||||||
|
/// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true
|
||||||
|
virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0;
|
||||||
|
/// Queues a fence into the backend if the fence isn't stubbed.
|
||||||
|
virtual void QueueFence(TFence& fence) = 0;
|
||||||
|
/// 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.
|
||||||
|
virtual void WaitFence(TFence& fence) = 0;
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
VideoCore::RasterizerInterface& rasterizer;
|
||||||
|
TTextureCache& texture_cache;
|
||||||
|
TTBufferCache& buffer_cache;
|
||||||
|
TQueryCache& query_cache;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void TryReleasePendingFences() {
|
||||||
|
auto& gpu{system.GPU()};
|
||||||
|
auto& memory_manager{gpu.MemoryManager()};
|
||||||
|
while (!fences.empty()) {
|
||||||
|
TFence& current_fence = fences.front();
|
||||||
|
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PopAsyncFlushes();
|
||||||
|
if (current_fence->IsSemaphore()) {
|
||||||
|
memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
|
||||||
|
} else {
|
||||||
|
gpu.IncrementSyncPoint(current_fence->GetPayload());
|
||||||
|
}
|
||||||
|
fences.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldWait() const {
|
||||||
|
return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
|
||||||
|
query_cache.ShouldWaitAsyncFlushes();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldFlush() const {
|
||||||
|
return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() ||
|
||||||
|
query_cache.HasUncommittedFlushes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopAsyncFlushes() {
|
||||||
|
texture_cache.PopAsyncFlushes();
|
||||||
|
buffer_cache.PopAsyncFlushes();
|
||||||
|
query_cache.PopAsyncFlushes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommitAsyncFlushes() {
|
||||||
|
texture_cache.CommitAsyncFlushes();
|
||||||
|
buffer_cache.CommitAsyncFlushes();
|
||||||
|
query_cache.CommitAsyncFlushes();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::queue<TFence> fences;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
|
||||||
|
#include "video_core/renderer_opengl/gl_fence_manager.h"
|
||||||
|
|
||||||
|
namespace OpenGL {
|
||||||
|
|
||||||
|
GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed)
|
||||||
|
: VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
|
||||||
|
|
||||||
|
GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
|
||||||
|
: VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {}
|
||||||
|
|
||||||
|
GLInnerFence::~GLInnerFence() = default;
|
||||||
|
|
||||||
|
void GLInnerFence::Queue() {
|
||||||
|
if (is_stubbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(sync_object.handle == 0);
|
||||||
|
sync_object.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLInnerFence::IsSignaled() const {
|
||||||
|
if (is_stubbed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ASSERT(sync_object.handle != 0);
|
||||||
|
GLsizei length;
|
||||||
|
GLint sync_status;
|
||||||
|
glGetSynciv(sync_object.handle, GL_SYNC_STATUS, sizeof(GLint), &length, &sync_status);
|
||||||
|
return sync_status == GL_SIGNALED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLInnerFence::Wait() {
|
||||||
|
if (is_stubbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(sync_object.handle != 0);
|
||||||
|
glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system,
|
||||||
|
VideoCore::RasterizerInterface& rasterizer,
|
||||||
|
TextureCacheOpenGL& texture_cache,
|
||||||
|
OGLBufferCache& buffer_cache, QueryCache& query_cache)
|
||||||
|
: GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache) {}
|
||||||
|
|
||||||
|
Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) {
|
||||||
|
return std::make_shared<GLInnerFence>(value, is_stubbed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
|
||||||
|
return std::make_shared<GLInnerFence>(addr, value, is_stubbed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FenceManagerOpenGL::QueueFence(Fence& fence) {
|
||||||
|
fence->Queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FenceManagerOpenGL::IsFenceSignaled(Fence& fence) const {
|
||||||
|
return fence->IsSignaled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FenceManagerOpenGL::WaitFence(Fence& fence) {
|
||||||
|
fence->Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OpenGL
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/fence_manager.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_query_cache.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||||
|
|
||||||
|
namespace OpenGL {
|
||||||
|
|
||||||
|
class GLInnerFence : public VideoCommon::FenceBase {
|
||||||
|
public:
|
||||||
|
GLInnerFence(u32 payload, bool is_stubbed);
|
||||||
|
GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed);
|
||||||
|
~GLInnerFence();
|
||||||
|
|
||||||
|
void Queue();
|
||||||
|
|
||||||
|
bool IsSignaled() const;
|
||||||
|
|
||||||
|
void Wait();
|
||||||
|
|
||||||
|
private:
|
||||||
|
OGLSync sync_object;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Fence = std::shared_ptr<GLInnerFence>;
|
||||||
|
using GenericFenceManager =
|
||||||
|
VideoCommon::FenceManager<Fence, TextureCacheOpenGL, OGLBufferCache, QueryCache>;
|
||||||
|
|
||||||
|
class FenceManagerOpenGL final : public GenericFenceManager {
|
||||||
|
public:
|
||||||
|
FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||||
|
TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache,
|
||||||
|
QueryCache& query_cache);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Fence CreateFence(u32 value, bool is_stubbed) override;
|
||||||
|
Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
|
||||||
|
void QueueFence(Fence& fence) override;
|
||||||
|
bool IsFenceSignaled(Fence& fence) const override;
|
||||||
|
void WaitFence(Fence& fence) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OpenGL
|
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_device.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_fence_manager.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||||
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload, bool is_stubbed)
|
||||||
|
: VideoCommon::FenceBase(payload, is_stubbed), device{device}, scheduler{scheduler} {}
|
||||||
|
|
||||||
|
InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address,
|
||||||
|
u32 payload, bool is_stubbed)
|
||||||
|
: VideoCommon::FenceBase(address, payload, is_stubbed), device{device}, scheduler{scheduler} {}
|
||||||
|
|
||||||
|
InnerFence::~InnerFence() = default;
|
||||||
|
|
||||||
|
void InnerFence::Queue() {
|
||||||
|
if (is_stubbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(!event);
|
||||||
|
|
||||||
|
event = device.GetLogical().CreateEvent();
|
||||||
|
ticks = scheduler.Ticks();
|
||||||
|
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
|
||||||
|
cmdbuf.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InnerFence::IsSignaled() const {
|
||||||
|
if (is_stubbed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ASSERT(event);
|
||||||
|
return IsEventSignalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerFence::Wait() {
|
||||||
|
if (is_stubbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(event);
|
||||||
|
|
||||||
|
if (ticks >= scheduler.Ticks()) {
|
||||||
|
scheduler.Flush();
|
||||||
|
}
|
||||||
|
while (!IsEventSignalled()) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InnerFence::IsEventSignalled() const {
|
||||||
|
switch (const VkResult result = event.GetStatus()) {
|
||||||
|
case VK_EVENT_SET:
|
||||||
|
return true;
|
||||||
|
case VK_EVENT_RESET:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
throw vk::Exception(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VKFenceManager::VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||||
|
const VKDevice& device, VKScheduler& scheduler,
|
||||||
|
VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
|
||||||
|
VKQueryCache& query_cache)
|
||||||
|
: GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache),
|
||||||
|
device{device}, scheduler{scheduler} {}
|
||||||
|
|
||||||
|
Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) {
|
||||||
|
return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
|
||||||
|
return std::make_shared<InnerFence>(device, scheduler, addr, value, is_stubbed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKFenceManager::QueueFence(Fence& fence) {
|
||||||
|
fence->Queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VKFenceManager::IsFenceSignaled(Fence& fence) const {
|
||||||
|
return fence->IsSignaled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKFenceManager::WaitFence(Fence& fence) {
|
||||||
|
fence->Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "video_core/fence_manager.h"
|
||||||
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
class RasterizerInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
class VKBufferCache;
|
||||||
|
class VKDevice;
|
||||||
|
class VKQueryCache;
|
||||||
|
class VKScheduler;
|
||||||
|
class VKTextureCache;
|
||||||
|
|
||||||
|
class InnerFence : public VideoCommon::FenceBase {
|
||||||
|
public:
|
||||||
|
explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload,
|
||||||
|
bool is_stubbed);
|
||||||
|
explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address,
|
||||||
|
u32 payload, bool is_stubbed);
|
||||||
|
~InnerFence();
|
||||||
|
|
||||||
|
void Queue();
|
||||||
|
|
||||||
|
bool IsSignaled() const;
|
||||||
|
|
||||||
|
void Wait();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool IsEventSignalled() const;
|
||||||
|
|
||||||
|
const VKDevice& device;
|
||||||
|
VKScheduler& scheduler;
|
||||||
|
vk::Event event;
|
||||||
|
u64 ticks = 0;
|
||||||
|
};
|
||||||
|
using Fence = std::shared_ptr<InnerFence>;
|
||||||
|
|
||||||
|
using GenericFenceManager =
|
||||||
|
VideoCommon::FenceManager<Fence, VKTextureCache, VKBufferCache, VKQueryCache>;
|
||||||
|
|
||||||
|
class VKFenceManager final : public GenericFenceManager {
|
||||||
|
public:
|
||||||
|
explicit VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||||
|
const VKDevice& device, VKScheduler& scheduler,
|
||||||
|
VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
|
||||||
|
VKQueryCache& query_cache);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Fence CreateFence(u32 value, bool is_stubbed) override;
|
||||||
|
Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
|
||||||
|
void QueueFence(Fence& fence) override;
|
||||||
|
bool IsFenceSignaled(Fence& fence) const override;
|
||||||
|
void WaitFence(Fence& fence) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const VKDevice& device;
|
||||||
|
VKScheduler& scheduler;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
Loading…
Reference in New Issue