Browse Source
Merge pull request #3677 from FernandoS27/better-sync
Merge pull request #3677 from FernandoS27/better-sync
Introduce Predictive Flushing and Improve ASYNC GPUpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1193 additions and 63 deletions
-
10src/core/settings.cpp
-
11src/core/settings.h
-
16src/core/telemetry_session.cpp
-
5src/video_core/CMakeLists.txt
-
119src/video_core/buffer_cache/buffer_cache.h
-
18src/video_core/buffer_cache/map_interval.h
-
3src/video_core/dma_pusher.cpp
-
8src/video_core/engines/maxwell_3d.cpp
-
11src/video_core/engines/maxwell_dma.cpp
-
170src/video_core/fence_manager.h
-
29src/video_core/gpu.cpp
-
29src/video_core/gpu.h
-
4src/video_core/gpu_asynch.cpp
-
2src/video_core/gpu_asynch.h
-
39src/video_core/gpu_thread.cpp
-
11src/video_core/gpu_thread.h
-
46src/video_core/query_cache.h
-
18src/video_core/rasterizer_interface.h
-
3src/video_core/renderer_opengl/gl_buffer_cache.cpp
-
72src/video_core/renderer_opengl/gl_fence_manager.cpp
-
53src/video_core/renderer_opengl/gl_fence_manager.h
-
62src/video_core/renderer_opengl/gl_rasterizer.cpp
-
10src/video_core/renderer_opengl/gl_rasterizer.h
-
17src/video_core/renderer_opengl/gl_shader_cache.cpp
-
3src/video_core/renderer_opengl/gl_shader_cache.h
-
101src/video_core/renderer_vulkan/vk_fence_manager.cpp
-
74src/video_core/renderer_vulkan/vk_fence_manager.h
-
16src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
-
3src/video_core/renderer_vulkan/vk_pipeline_cache.h
-
55src/video_core/renderer_vulkan/vk_rasterizer.cpp
-
8src/video_core/renderer_vulkan/vk_rasterizer.h
-
18src/video_core/renderer_vulkan/wrapper.cpp
-
20src/video_core/renderer_vulkan/wrapper.h
-
18src/video_core/texture_cache/surface_base.h
-
114src/video_core/texture_cache/texture_cache.h
-
8src/yuzu/configuration/config.cpp
-
5src/yuzu/configuration/configure_graphics_advanced.cpp
-
33src/yuzu/configuration/configure_graphics_advanced.ui
-
4src/yuzu_cmd/config.cpp
-
6src/yuzu_cmd/default_ini.h
-
4src/yuzu_tester/config.cpp
@ -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 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue