Frontend/GPU: Refactor context management
Changes the GraphicsContext to be managed by the GPU core. This eliminates the need for the frontends to fool around with tricky MakeCurrent/DoneCurrent calls that are dependent on the settings (such as async gpu option). This also refactors out the need to use QWidget::fromWindowContainer as that caused issues with focus and input handling. Now we use a regular QWidget and just access the native windowHandle() directly. Another change is removing the debug tool setting in FrameMailbox. Instead of trying to block the frontend until a new frame is ready, the core will now take over presentation and draw directly to the window if the renderer detects that its hooked by NSight or RenderDoc Lastly, since it was in the way, I removed ScopeAcquireWindowContext and replaced it with a simple subclass in GraphicsContext that achieves the same result
This commit is contained in:
parent
b3f33a2c6b
commit
b37d69e5e1
29 changed files with 361 additions and 418 deletions
|
|
@ -131,8 +131,6 @@ add_library(core STATIC
|
|||
frontend/framebuffer_layout.cpp
|
||||
frontend/framebuffer_layout.h
|
||||
frontend/input.h
|
||||
frontend/scope_acquire_context.cpp
|
||||
frontend/scope_acquire_context.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hardware_interrupt_manager.cpp
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
|
|
@ -168,13 +167,9 @@ struct System::Impl {
|
|||
Service::Init(service_manager, system);
|
||||
GDBStub::Init();
|
||||
|
||||
renderer = VideoCore::CreateRenderer(emu_window, system);
|
||||
if (!renderer->Init()) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(system);
|
||||
renderer->Rasterizer().SetupDirtyFlags();
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
|
|
@ -186,7 +181,6 @@ struct System::Impl {
|
|||
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
|
||||
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
if (!app_loader) {
|
||||
|
|
@ -216,10 +210,6 @@ struct System::Impl {
|
|||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
// Main process has been loaded and been made current.
|
||||
// Begin GPU and CPU execution.
|
||||
gpu_core->Start();
|
||||
|
||||
// Initialize cheat engine
|
||||
if (cheat_engine) {
|
||||
cheat_engine->Initialize();
|
||||
|
|
@ -277,7 +267,6 @@ struct System::Impl {
|
|||
}
|
||||
|
||||
// Shutdown emulation session
|
||||
renderer.reset();
|
||||
GDBStub::Shutdown();
|
||||
Service::Shutdown();
|
||||
service_manager.reset();
|
||||
|
|
@ -353,7 +342,6 @@ struct System::Impl {
|
|||
Service::FileSystem::FileSystemController fs_controller;
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
Memory::Memory memory;
|
||||
|
|
@ -536,11 +524,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {
|
|||
}
|
||||
|
||||
VideoCore::RendererBase& System::Renderer() {
|
||||
return *impl->renderer;
|
||||
return impl->gpu_core->Renderer();
|
||||
}
|
||||
|
||||
const VideoCore::RendererBase& System::Renderer() const {
|
||||
return *impl->renderer;
|
||||
return impl->gpu_core->Renderer();
|
||||
}
|
||||
|
||||
Kernel::KernelCore& System::Kernel() {
|
||||
|
|
|
|||
|
|
@ -13,19 +13,39 @@
|
|||
namespace Core::Frontend {
|
||||
|
||||
/**
|
||||
* Represents a graphics context that can be used for background computation or drawing. If the
|
||||
* graphics backend doesn't require the context, then the implementation of these methods can be
|
||||
* stubs
|
||||
* Represents a drawing context that supports graphics operations.
|
||||
*/
|
||||
class GraphicsContext {
|
||||
public:
|
||||
virtual ~GraphicsContext();
|
||||
|
||||
/// Inform the driver to swap the front/back buffers and present the current image
|
||||
virtual void SwapBuffers() {}
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
virtual void MakeCurrent() {}
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
virtual void DoneCurrent() {}
|
||||
|
||||
class Scoped {
|
||||
public:
|
||||
Scoped(GraphicsContext& context_) : context(context_) {
|
||||
context.MakeCurrent();
|
||||
}
|
||||
~Scoped() {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
|
||||
private:
|
||||
GraphicsContext& context;
|
||||
};
|
||||
|
||||
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
|
||||
/// ends
|
||||
Scoped Acquire() {
|
||||
return Scoped{*this};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -46,7 +66,7 @@ public:
|
|||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||
* re-read the upper points again and think about it if you don't see this.
|
||||
*/
|
||||
class EmuWindow : public GraphicsContext {
|
||||
class EmuWindow {
|
||||
public:
|
||||
/// Data structure to store emuwindow configuration
|
||||
struct WindowConfig {
|
||||
|
|
@ -60,17 +80,9 @@ public:
|
|||
virtual void PollEvents() = 0;
|
||||
|
||||
/**
|
||||
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
|
||||
* context can be used from other threads for background graphics computation. If the frontend
|
||||
* is using a graphics backend that doesn't need anything specific to run on a different thread,
|
||||
* then it can use a stubbed implemenation for GraphicsContext.
|
||||
*
|
||||
* If the return value is null, then the core should assume that the frontend cannot provide a
|
||||
* Shared Context
|
||||
* Returns a GraphicsContext that the frontend provides to be used for rendering.
|
||||
*/
|
||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;
|
||||
|
||||
/// Returns if window is shown (not minimized)
|
||||
virtual bool IsShown() const = 0;
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
|
||||
: context{context} {
|
||||
context.MakeCurrent();
|
||||
}
|
||||
ScopeAcquireContext::~ScopeAcquireContext() {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class GraphicsContext;
|
||||
|
||||
/// Helper class to acquire/release window context within a given scope
|
||||
class ScopeAcquireContext : NonCopyable {
|
||||
public:
|
||||
explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
|
||||
~ScopeAcquireContext();
|
||||
|
||||
private:
|
||||
Core::Frontend::GraphicsContext& context;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
|
|
@ -16,14 +17,15 @@
|
|||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
|
||||
|
||||
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
|
||||
: system{system}, renderer{renderer}, is_async{is_async} {
|
||||
auto& rasterizer{renderer.Rasterizer()};
|
||||
GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async)
|
||||
: system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
|
||||
auto& rasterizer{renderer->Rasterizer()};
|
||||
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
|
||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
||||
|
|
@ -137,7 +139,7 @@ u64 GPU::GetTicks() const {
|
|||
}
|
||||
|
||||
void GPU::FlushCommands() {
|
||||
renderer.Rasterizer().FlushCommands();
|
||||
renderer->Rasterizer().FlushCommands();
|
||||
}
|
||||
|
||||
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) {
|
|||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
namespace Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
|
|
@ -129,7 +132,8 @@ class MemoryManager;
|
|||
|
||||
class GPU {
|
||||
public:
|
||||
explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async);
|
||||
explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||
bool is_async);
|
||||
|
||||
virtual ~GPU();
|
||||
|
||||
|
|
@ -174,6 +178,14 @@ public:
|
|||
/// Returns a reference to the GPU DMA pusher.
|
||||
Tegra::DmaPusher& DmaPusher();
|
||||
|
||||
VideoCore::RendererBase& Renderer() {
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
const VideoCore::RendererBase& Renderer() const {
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
// Waits for the GPU to finish working
|
||||
virtual void WaitIdle() const = 0;
|
||||
|
||||
|
|
@ -287,7 +299,7 @@ private:
|
|||
protected:
|
||||
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
|
||||
Core::System& system;
|
||||
VideoCore::RendererBase& renderer;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,16 @@
|
|||
|
||||
namespace VideoCommon {
|
||||
|
||||
GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
|
||||
: GPU(system, renderer, true), gpu_thread{system} {}
|
||||
GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_,
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
|
||||
: GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)),
|
||||
cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {}
|
||||
|
||||
GPUAsynch::~GPUAsynch() = default;
|
||||
|
||||
void GPUAsynch::Start() {
|
||||
gpu_thread.StartThread(renderer, *dma_pusher);
|
||||
cpu_context->MakeCurrent();
|
||||
gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);
|
||||
}
|
||||
|
||||
void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
#include "video_core/gpu.h"
|
||||
#include "video_core/gpu_thread.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class GraphicsContext;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
} // namespace VideoCore
|
||||
|
|
@ -16,7 +20,8 @@ namespace VideoCommon {
|
|||
/// Implementation of GPU interface that runs the GPU asynchronously
|
||||
class GPUAsynch final : public Tegra::GPU {
|
||||
public:
|
||||
explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
|
||||
explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
|
||||
~GPUAsynch() override;
|
||||
|
||||
void Start() override;
|
||||
|
|
@ -32,6 +37,8 @@ protected:
|
|||
|
||||
private:
|
||||
GPUThread::ThreadManager gpu_thread;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@
|
|||
|
||||
namespace VideoCommon {
|
||||
|
||||
GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
|
||||
: GPU(system, renderer, false) {}
|
||||
GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
|
||||
: GPU(system, std::move(renderer), false), context{std::move(context)} {}
|
||||
|
||||
GPUSynch::~GPUSynch() = default;
|
||||
|
||||
void GPUSynch::Start() {}
|
||||
void GPUSynch::Start() {
|
||||
context->MakeCurrent();
|
||||
}
|
||||
|
||||
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||
dma_pusher->Push(std::move(entries));
|
||||
|
|
@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
|||
}
|
||||
|
||||
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
renderer.SwapBuffers(framebuffer);
|
||||
renderer->SwapBuffers(framebuffer);
|
||||
}
|
||||
|
||||
void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
|
||||
renderer.Rasterizer().FlushRegion(addr, size);
|
||||
renderer->Rasterizer().FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
|
||||
renderer.Rasterizer().InvalidateRegion(addr, size);
|
||||
renderer->Rasterizer().InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
||||
renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
|
||||
renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class GraphicsContext;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
} // namespace VideoCore
|
||||
|
|
@ -15,7 +19,8 @@ namespace VideoCommon {
|
|||
/// Implementation of GPU interface that runs the GPU synchronously
|
||||
class GPUSynch final : public Tegra::GPU {
|
||||
public:
|
||||
explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
|
||||
explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
|
||||
~GPUSynch() override;
|
||||
|
||||
void Start() override;
|
||||
|
|
@ -29,6 +34,9 @@ public:
|
|||
protected:
|
||||
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
|
||||
[[maybe_unused]] u32 value) const override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> context;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/gpu_thread.h"
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
namespace VideoCommon::GPUThread {
|
||||
|
||||
/// Runs the GPU thread
|
||||
static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
|
||||
SynchState& state) {
|
||||
static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher, SynchState& state) {
|
||||
MicroProfileOnThreadCreate("GpuThread");
|
||||
|
||||
// Wait for first GPU command before acquiring the window context
|
||||
|
|
@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
|||
return;
|
||||
}
|
||||
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
|
||||
auto current_context = context.Acquire();
|
||||
|
||||
CommandDataContainer next;
|
||||
while (state.is_running) {
|
||||
|
|
@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() {
|
|||
thread.join();
|
||||
}
|
||||
|
||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
|
||||
thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
|
||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher) {
|
||||
thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher),
|
||||
std::ref(state)};
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <optional>
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
|
|
@ -20,6 +19,9 @@ class DmaPusher;
|
|||
} // namespace Tegra
|
||||
|
||||
namespace Core {
|
||||
namespace Frontend {
|
||||
class GraphicsContext;
|
||||
}
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
|
|
@ -99,7 +101,8 @@ public:
|
|||
~ThreadManager();
|
||||
|
||||
/// Creates and starts the GPU thread.
|
||||
void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
|
||||
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher);
|
||||
|
||||
/// Push GPU command entries to be processed
|
||||
void SubmitList(Tegra::CommandList&& entries);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ public:
|
|||
|
||||
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
|
||||
/// specific implementation)
|
||||
virtual void TryPresent(int timeout_ms) = 0;
|
||||
/// Returns true if a frame was drawn
|
||||
virtual bool TryPresent(int timeout_ms) = 0;
|
||||
|
||||
// Getter/setter functions:
|
||||
// ------------------------
|
||||
|
|
|
|||
|
|
@ -305,7 +305,6 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||
}
|
||||
|
||||
const std::vector gl_cache = disk_cache.LoadPrecompiled();
|
||||
const auto supported_formats = GetSupportedFormats();
|
||||
|
||||
// Track if precompiled cache was altered during loading to know if we have to
|
||||
// serialize the virtual precompiled cache file back to the hard drive
|
||||
|
|
@ -327,8 +326,8 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||
|
||||
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
||||
std::size_t end) {
|
||||
context->MakeCurrent();
|
||||
SCOPE_EXIT({ return context->DoneCurrent(); });
|
||||
const auto scope = context->Acquire();
|
||||
const auto supported_formats = GetSupportedFormats();
|
||||
|
||||
for (std::size_t i = begin; i < end; ++i) {
|
||||
if (stop_loading) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
|
|
@ -30,8 +28,6 @@ namespace OpenGL {
|
|||
|
||||
namespace {
|
||||
|
||||
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
|
||||
// to wait on available presentation frames.
|
||||
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
|
||||
|
||||
struct Frame {
|
||||
|
|
@ -214,7 +210,7 @@ public:
|
|||
std::deque<Frame*> present_queue;
|
||||
Frame* previous_frame{};
|
||||
|
||||
FrameMailbox() : has_debug_tool{HasDebugTool()} {
|
||||
FrameMailbox() {
|
||||
for (auto& frame : swap_chain) {
|
||||
free_queue.push(&frame);
|
||||
}
|
||||
|
|
@ -285,13 +281,9 @@ public:
|
|||
std::unique_lock lock{swap_chain_lock};
|
||||
present_queue.push_front(frame);
|
||||
present_cv.notify_one();
|
||||
|
||||
DebugNotifyNextFrame();
|
||||
}
|
||||
|
||||
Frame* TryGetPresentFrame(int timeout_ms) {
|
||||
DebugWaitForNextFrame();
|
||||
|
||||
std::unique_lock lock{swap_chain_lock};
|
||||
// wait for new entries in the present_queue
|
||||
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
||||
|
|
@ -317,38 +309,12 @@ public:
|
|||
previous_frame = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex debug_synch_mutex;
|
||||
std::condition_variable debug_synch_condition;
|
||||
std::atomic_int frame_for_debug{};
|
||||
const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step
|
||||
|
||||
/// Signal that a new frame is available (called from GPU thread)
|
||||
void DebugNotifyNextFrame() {
|
||||
if (!has_debug_tool) {
|
||||
return;
|
||||
}
|
||||
frame_for_debug++;
|
||||
std::lock_guard lock{debug_synch_mutex};
|
||||
debug_synch_condition.notify_one();
|
||||
}
|
||||
|
||||
/// Wait for a new frame to be available (called from presentation thread)
|
||||
void DebugWaitForNextFrame() {
|
||||
if (!has_debug_tool) {
|
||||
return;
|
||||
}
|
||||
const int last_frame = frame_for_debug;
|
||||
std::unique_lock lock{debug_synch_mutex};
|
||||
debug_synch_condition.wait(lock,
|
||||
[this, last_frame] { return frame_for_debug > last_frame; });
|
||||
}
|
||||
};
|
||||
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
|
||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
|
||||
frame_mailbox{std::make_unique<FrameMailbox>()} {}
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
|
||||
Core::Frontend::GraphicsContext& context)
|
||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, frame_mailbox{},
|
||||
has_debug_tool{HasDebugTool()}, context{context} {}
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
|
|
@ -356,8 +322,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12
|
|||
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
||||
|
||||
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
render_window.PollEvents();
|
||||
|
||||
if (!framebuffer) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -413,6 +377,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
|||
m_current_frame++;
|
||||
rasterizer->TickFrame();
|
||||
}
|
||||
|
||||
render_window.PollEvents();
|
||||
if (has_debug_tool) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
Present(0);
|
||||
context.SwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
|
||||
|
|
@ -480,6 +451,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
|||
}
|
||||
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
frame_mailbox = std::make_unique<FrameMailbox>();
|
||||
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
|
||||
|
|
@ -692,12 +665,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
|||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
bool RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
if (has_debug_tool) {
|
||||
LOG_DEBUG(Render_OpenGL,
|
||||
"Skipping presentation because we are presenting on the main context");
|
||||
return false;
|
||||
}
|
||||
return Present(timeout_ms);
|
||||
}
|
||||
|
||||
bool RendererOpenGL::Present(int timeout_ms) {
|
||||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
|
||||
if (!frame) {
|
||||
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
|
||||
|
|
@ -725,6 +707,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
|
|||
glFlush();
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererOpenGL::RenderScreenshot() {
|
||||
|
|
|
|||
|
|
@ -55,13 +55,14 @@ class FrameMailbox;
|
|||
|
||||
class RendererOpenGL final : public VideoCore::RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
|
||||
Core::Frontend::GraphicsContext& context);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
bool Init() override;
|
||||
void ShutDown() override;
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||
void TryPresent(int timeout_ms) override;
|
||||
bool TryPresent(int timeout_ms) override;
|
||||
|
||||
private:
|
||||
/// Initializes the OpenGL state and creates persistent objects.
|
||||
|
|
@ -89,8 +90,11 @@ private:
|
|||
|
||||
void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
|
||||
|
||||
bool Present(int timeout_ms);
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
Core::System& system;
|
||||
Core::Frontend::GraphicsContext& context;
|
||||
|
||||
StateTracker state_tracker{system};
|
||||
|
||||
|
|
@ -115,6 +119,8 @@ private:
|
|||
|
||||
/// Frame presentation mailbox
|
||||
std::unique_ptr<FrameMailbox> frame_mailbox;
|
||||
|
||||
bool has_debug_tool = false;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
|
|||
|
|
@ -141,8 +141,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
|||
render_window.PollEvents();
|
||||
}
|
||||
|
||||
void RendererVulkan::TryPresent(int /*timeout_ms*/) {
|
||||
bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
|
||||
// TODO (bunnei): ImplementMe
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RendererVulkan::Init() {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public:
|
|||
bool Init() override;
|
||||
void ShutDown() override;
|
||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||
void TryPresent(int timeout_ms) override;
|
||||
bool TryPresent(int timeout_ms) override;
|
||||
|
||||
private:
|
||||
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@
|
|||
#endif
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system) {
|
||||
namespace {
|
||||
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system,
|
||||
Core::Frontend::GraphicsContext& context) {
|
||||
switch (Settings::values.renderer_backend) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
|
||||
#ifdef HAS_VULKAN
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
|
||||
|
|
@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
|
||||
namespace VideoCore {
|
||||
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
||||
auto context = emu_window.CreateSharedContext();
|
||||
const auto scope = context->Acquire();
|
||||
auto renderer = CreateRenderer(emu_window, system, *context);
|
||||
if (!renderer->Init()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
|
||||
std::move(context));
|
||||
}
|
||||
return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
|
||||
}
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue