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