Merge pull request #3552 from jroweboy/single-context

Refactor Context management (Fixes renderdoc on opengl issues)
This commit is contained in:
Rodrigo Locatti 2020-04-02 01:38:25 -03:00 committed by GitHub
commit 825a6e2615
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 388 additions and 453 deletions

View file

@ -131,8 +131,6 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h frontend/framebuffer_layout.h
frontend/input.h frontend/input.h
frontend/scope_acquire_context.cpp
frontend/scope_acquire_context.h
gdbstub/gdbstub.cpp gdbstub/gdbstub.cpp
gdbstub/gdbstub.h gdbstub/gdbstub.h
hardware_interrupt_manager.cpp hardware_interrupt_manager.cpp

View file

@ -24,7 +24,6 @@
#include "core/file_sys/sdmc_factory.h" #include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h" #include "core/file_sys/vfs_real.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h" #include "core/gdbstub/gdbstub.h"
#include "core/hardware_interrupt_manager.h" #include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
@ -168,13 +167,12 @@ struct System::Impl {
Service::Init(service_manager, system); Service::Init(service_manager, system);
GDBStub::DeferStart(); GDBStub::DeferStart();
renderer = VideoCore::CreateRenderer(emu_window, system); interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
if (!renderer->Init()) { gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
return ResultStatus::ErrorVideoCore; return ResultStatus::ErrorVideoCore;
} }
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
gpu_core = VideoCore::CreateGPU(system);
renderer->Rasterizer().SetupDirtyFlags();
is_powered_on = true; is_powered_on = true;
exit_lock = false; exit_lock = false;
@ -186,8 +184,6 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) { const std::string& filepath) {
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) { if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@ -216,10 +212,6 @@ struct System::Impl {
AddGlueRegistrationForProcess(*app_loader, *main_process); AddGlueRegistrationForProcess(*app_loader, *main_process);
kernel.MakeCurrentProcess(main_process.get()); 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 // Initialize cheat engine
if (cheat_engine) { if (cheat_engine) {
cheat_engine->Initialize(); cheat_engine->Initialize();
@ -277,7 +269,6 @@ struct System::Impl {
} }
// Shutdown emulation session // Shutdown emulation session
renderer.reset();
GDBStub::Shutdown(); GDBStub::Shutdown();
Service::Shutdown(); Service::Shutdown();
service_manager.reset(); service_manager.reset();
@ -353,7 +344,6 @@ struct System::Impl {
Service::FileSystem::FileSystemController fs_controller; Service::FileSystem::FileSystemController fs_controller;
/// AppLoader used to load the current executing application /// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader; std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core; std::unique_ptr<Tegra::GPU> gpu_core;
std::unique_ptr<Hardware::InterruptManager> interrupt_manager; std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
Memory::Memory memory; Memory::Memory memory;
@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {
} }
VideoCore::RendererBase& System::Renderer() { VideoCore::RendererBase& System::Renderer() {
return *impl->renderer; return impl->gpu_core->Renderer();
} }
const VideoCore::RendererBase& System::Renderer() const { const VideoCore::RendererBase& System::Renderer() const {
return *impl->renderer; return impl->gpu_core->Renderer();
} }
Kernel::KernelCore& System::Kernel() { Kernel::KernelCore& System::Kernel() {

View file

@ -13,19 +13,39 @@
namespace Core::Frontend { namespace Core::Frontend {
/** /**
* Represents a graphics context that can be used for background computation or drawing. If the * Represents a drawing context that supports graphics operations.
* graphics backend doesn't require the context, then the implementation of these methods can be
* stubs
*/ */
class GraphicsContext { class GraphicsContext {
public: public:
virtual ~GraphicsContext(); 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 /// 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 /// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0; virtual void DoneCurrent() {}
class Scoped {
public:
explicit 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 * - 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. * re-read the upper points again and think about it if you don't see this.
*/ */
class EmuWindow : public GraphicsContext { class EmuWindow {
public: public:
/// Data structure to store emuwindow configuration /// Data structure to store emuwindow configuration
struct WindowConfig { struct WindowConfig {
@ -60,17 +80,9 @@ public:
virtual void PollEvents() = 0; virtual void PollEvents() = 0;
/** /**
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This * Returns a GraphicsContext that the frontend provides to be used for rendering.
* 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
*/ */
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;
return nullptr;
}
/// Returns if window is shown (not minimized) /// Returns if window is shown (not minimized)
virtual bool IsShown() const = 0; virtual bool IsShown() const = 0;

View file

@ -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

View file

@ -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

View file

@ -7,6 +7,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/core_timing_util.h" #include "core/core_timing_util.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/engines/fermi_2d.h" #include "video_core/engines/fermi_2d.h"
#include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_compute.h"
@ -16,14 +17,15 @@
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Tegra { namespace Tegra {
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 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) GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async)
: system{system}, renderer{renderer}, is_async{is_async} { : system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
auto& rasterizer{renderer.Rasterizer()}; auto& rasterizer{renderer->Rasterizer()};
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
@ -137,7 +139,7 @@ u64 GPU::GetTicks() const {
} }
void GPU::FlushCommands() { void GPU::FlushCommands() {
renderer.Rasterizer().FlushCommands(); renderer->Rasterizer().FlushCommands();
} }
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence

View file

@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) {
} }
namespace Core { namespace Core {
class System; namespace Frontend {
class EmuWindow;
} }
class System;
} // namespace Core
namespace VideoCore { namespace VideoCore {
class RendererBase; class RendererBase;
@ -129,7 +132,8 @@ class MemoryManager;
class GPU { class GPU {
public: 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(); virtual ~GPU();
@ -174,6 +178,14 @@ public:
/// Returns a reference to the GPU DMA pusher. /// Returns a reference to the GPU DMA pusher.
Tegra::DmaPusher& DmaPusher(); Tegra::DmaPusher& DmaPusher();
VideoCore::RendererBase& Renderer() {
return *renderer;
}
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
// Waits for the GPU to finish working // Waits for the GPU to finish working
virtual void WaitIdle() const = 0; virtual void WaitIdle() const = 0;
@ -287,7 +299,7 @@ private:
protected: protected:
std::unique_ptr<Tegra::DmaPusher> dma_pusher; std::unique_ptr<Tegra::DmaPusher> dma_pusher;
Core::System& system; Core::System& system;
VideoCore::RendererBase& renderer; std::unique_ptr<VideoCore::RendererBase> renderer;
private: private:
std::unique_ptr<Tegra::MemoryManager> memory_manager; std::unique_ptr<Tegra::MemoryManager> memory_manager;

View file

@ -10,13 +10,16 @@
namespace VideoCommon { namespace VideoCommon {
GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_,
: GPU(system, renderer, true), gpu_thread{system} {} 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; GPUAsynch::~GPUAsynch() = default;
void GPUAsynch::Start() { 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) { void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {

View file

@ -7,6 +7,10 @@
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/gpu_thread.h" #include "video_core/gpu_thread.h"
namespace Core::Frontend {
class GraphicsContext;
}
namespace VideoCore { namespace VideoCore {
class RendererBase; class RendererBase;
} // namespace VideoCore } // namespace VideoCore
@ -16,7 +20,8 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU asynchronously /// Implementation of GPU interface that runs the GPU asynchronously
class GPUAsynch final : public Tegra::GPU { class GPUAsynch final : public Tegra::GPU {
public: 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; ~GPUAsynch() override;
void Start() override; void Start() override;
@ -32,6 +37,8 @@ protected:
private: private:
GPUThread::ThreadManager gpu_thread; GPUThread::ThreadManager gpu_thread;
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
}; };
} // namespace VideoCommon } // namespace VideoCommon

View file

@ -7,12 +7,15 @@
namespace VideoCommon { namespace VideoCommon {
GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
: GPU(system, renderer, false) {} std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
: GPU(system, std::move(renderer), false), context{std::move(context)} {}
GPUSynch::~GPUSynch() = default; GPUSynch::~GPUSynch() = default;
void GPUSynch::Start() {} void GPUSynch::Start() {
context->MakeCurrent();
}
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
dma_pusher->Push(std::move(entries)); dma_pusher->Push(std::move(entries));
@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
} }
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
renderer.SwapBuffers(framebuffer); renderer->SwapBuffers(framebuffer);
} }
void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
renderer.Rasterizer().FlushRegion(addr, size); renderer->Rasterizer().FlushRegion(addr, size);
} }
void GPUSynch::InvalidateRegion(CacheAddr addr, u64 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) { void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
} }
} // namespace VideoCommon } // namespace VideoCommon

View file

@ -6,6 +6,10 @@
#include "video_core/gpu.h" #include "video_core/gpu.h"
namespace Core::Frontend {
class GraphicsContext;
}
namespace VideoCore { namespace VideoCore {
class RendererBase; class RendererBase;
} // namespace VideoCore } // namespace VideoCore
@ -15,7 +19,8 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU synchronously /// Implementation of GPU interface that runs the GPU synchronously
class GPUSynch final : public Tegra::GPU { class GPUSynch final : public Tegra::GPU {
public: 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; ~GPUSynch() override;
void Start() override; void Start() override;
@ -29,6 +34,9 @@ public:
protected: protected:
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
[[maybe_unused]] u32 value) const override {} [[maybe_unused]] u32 value) const override {}
private:
std::unique_ptr<Core::Frontend::GraphicsContext> context;
}; };
} // namespace VideoCommon } // namespace VideoCommon

View file

@ -5,7 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "core/core.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/dma_pusher.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/gpu_thread.h" #include "video_core/gpu_thread.h"
@ -14,8 +14,8 @@
namespace VideoCommon::GPUThread { namespace VideoCommon::GPUThread {
/// Runs the GPU thread /// Runs the GPU thread
static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
SynchState& state) { Tegra::DmaPusher& dma_pusher, SynchState& state) {
MicroProfileOnThreadCreate("GpuThread"); MicroProfileOnThreadCreate("GpuThread");
// Wait for first GPU command before acquiring the window context // 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; return;
} }
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()}; auto current_context = context.Acquire();
CommandDataContainer next; CommandDataContainer next;
while (state.is_running) { while (state.is_running) {
@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() {
thread.join(); thread.join();
} }
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) { void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)}; 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) { void ThreadManager::SubmitList(Tegra::CommandList&& entries) {

View file

@ -10,7 +10,6 @@
#include <optional> #include <optional>
#include <thread> #include <thread>
#include <variant> #include <variant>
#include "common/threadsafe_queue.h" #include "common/threadsafe_queue.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
@ -20,6 +19,9 @@ class DmaPusher;
} // namespace Tegra } // namespace Tegra
namespace Core { namespace Core {
namespace Frontend {
class GraphicsContext;
}
class System; class System;
} // namespace Core } // namespace Core
@ -99,7 +101,8 @@ public:
~ThreadManager(); ~ThreadManager();
/// Creates and starts the GPU thread. /// 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 /// Push GPU command entries to be processed
void SubmitList(Tegra::CommandList&& entries); void SubmitList(Tegra::CommandList&& entries);

View file

@ -46,7 +46,8 @@ public:
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
/// specific implementation) /// 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: // Getter/setter functions:
// ------------------------ // ------------------------

View file

@ -327,8 +327,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
std::size_t end) { std::size_t end) {
context->MakeCurrent(); const auto scope = context->Acquire();
SCOPE_EXIT({ return context->DoneCurrent(); });
for (std::size_t i = begin; i < end; ++i) { for (std::size_t i = begin; i < end; ++i) {
if (stop_loading) { if (stop_loading) {

View file

@ -30,8 +30,6 @@ namespace OpenGL {
namespace { 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; constexpr std::size_t SWAP_CHAIN_SIZE = 3;
struct Frame { struct Frame {
@ -214,7 +212,7 @@ public:
std::deque<Frame*> present_queue; std::deque<Frame*> present_queue;
Frame* previous_frame{}; Frame* previous_frame{};
FrameMailbox() : has_debug_tool{HasDebugTool()} { FrameMailbox() {
for (auto& frame : swap_chain) { for (auto& frame : swap_chain) {
free_queue.push(&frame); free_queue.push(&frame);
} }
@ -285,13 +283,9 @@ public:
std::unique_lock lock{swap_chain_lock}; std::unique_lock lock{swap_chain_lock};
present_queue.push_front(frame); present_queue.push_front(frame);
present_cv.notify_one(); present_cv.notify_one();
DebugNotifyNextFrame();
} }
Frame* TryGetPresentFrame(int timeout_ms) { Frame* TryGetPresentFrame(int timeout_ms) {
DebugWaitForNextFrame();
std::unique_lock lock{swap_chain_lock}; std::unique_lock lock{swap_chain_lock};
// wait for new entries in the present_queue // wait for new entries in the present_queue
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
@ -317,38 +311,12 @@ public:
previous_frame = frame; previous_frame = frame;
return 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) RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
Core::Frontend::GraphicsContext& context)
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
frame_mailbox{std::make_unique<FrameMailbox>()} {} frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {}
RendererOpenGL::~RendererOpenGL() = default; RendererOpenGL::~RendererOpenGL() = default;
@ -356,8 +324,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)); MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents();
if (!framebuffer) { if (!framebuffer) {
return; return;
} }
@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
m_current_frame++; m_current_frame++;
rasterizer->TickFrame(); 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) { void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
} }
void RendererOpenGL::InitOpenGLObjects() { void RendererOpenGL::InitOpenGLObjects() {
frame_mailbox = std::make_unique<FrameMailbox>();
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
0.0f); 0.0f);
@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 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(); const auto& layout = render_window.GetFramebufferLayout();
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
if (!frame) { if (!frame) {
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); 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 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
glFlush(); glFlush();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return true;
} }
void RendererOpenGL::RenderScreenshot() { void RendererOpenGL::RenderScreenshot() {

View file

@ -55,13 +55,14 @@ class FrameMailbox;
class RendererOpenGL final : public VideoCore::RendererBase { class RendererOpenGL final : public VideoCore::RendererBase {
public: 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; ~RendererOpenGL() override;
bool Init() override; bool Init() override;
void ShutDown() override; void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
void TryPresent(int timeout_ms) override; bool TryPresent(int timeout_ms) override;
private: private:
/// Initializes the OpenGL state and creates persistent objects. /// Initializes the OpenGL state and creates persistent objects.
@ -89,8 +90,11 @@ private:
void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
bool Present(int timeout_ms);
Core::Frontend::EmuWindow& emu_window; Core::Frontend::EmuWindow& emu_window;
Core::System& system; Core::System& system;
Core::Frontend::GraphicsContext& context;
StateTracker state_tracker{system}; StateTracker state_tracker{system};
@ -115,6 +119,8 @@ private:
/// Frame presentation mailbox /// Frame presentation mailbox
std::unique_ptr<FrameMailbox> frame_mailbox; std::unique_ptr<FrameMailbox> frame_mailbox;
bool has_debug_tool = false;
}; };
} // namespace OpenGL } // namespace OpenGL

View file

@ -141,8 +141,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents(); render_window.PollEvents();
} }
void RendererVulkan::TryPresent(int /*timeout_ms*/) { bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
// TODO (bunnei): ImplementMe // TODO (bunnei): ImplementMe
return true;
} }
bool RendererVulkan::Init() { bool RendererVulkan::Init() {

View file

@ -42,7 +42,7 @@ public:
bool Init() override; bool Init() override;
void ShutDown() override; void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
void TryPresent(int timeout_ms) override; bool TryPresent(int timeout_ms) override;
private: private:
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(

View file

@ -15,13 +15,13 @@
#endif #endif
#include "video_core/video_core.h" #include "video_core/video_core.h"
namespace VideoCore { namespace {
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, Core::System& system,
Core::System& system) { Core::Frontend::GraphicsContext& context) {
switch (Settings::values.renderer_backend) { switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL: 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 #ifdef HAS_VULKAN
case Settings::RendererBackend::Vulkan: case Settings::RendererBackend::Vulkan:
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); 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; return nullptr;
} }
} }
} // Anonymous namespace
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { namespace VideoCore {
if (Settings::values.use_asynchronous_gpu_emulation) {
return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer()); 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 nullptr;
} }
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) { u16 GetResolutionScaleFactor(const RendererBase& renderer) {

View file

@ -22,17 +22,8 @@ namespace VideoCore {
class RendererBase; class RendererBase;
/**
* Creates a renderer instance.
*
* @note The returned renderer instance is simply allocated. Its Init()
* function still needs to be called to fully complete its setup.
*/
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system);
/// Creates an emulated GPU instance using the given system context. /// Creates an emulated GPU instance using the given system context.
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system); std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
u16 GetResolutionScaleFactor(const RendererBase& renderer); u16 GetResolutionScaleFactor(const RendererBase& renderer);

View file

@ -10,9 +10,6 @@
#include <QMessageBox> #include <QMessageBox>
#include <QOffscreenSurface> #include <QOffscreenSurface>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_4_3_Core>
#include <QOpenGLWindow>
#include <QPainter> #include <QPainter>
#include <QScreen> #include <QScreen>
#include <QStringList> #include <QStringList>
@ -29,7 +26,6 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/settings.h" #include "core/settings.h"
#include "input_common/keyboard.h" #include "input_common/keyboard.h"
#include "input_common/main.h" #include "input_common/main.h"
@ -39,26 +35,16 @@
#include "yuzu/bootmanager.h" #include "yuzu/bootmanager.h"
#include "yuzu/main.h" #include "yuzu/main.h"
EmuThread::EmuThread(GRenderWindow& window) EmuThread::EmuThread() = default;
: shared_context{window.CreateSharedContext()},
context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
: window} {}
EmuThread::~EmuThread() = default; EmuThread::~EmuThread() = default;
static GMainWindow* GetMainWindow() {
for (QWidget* w : qApp->topLevelWidgets()) {
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
return main;
}
}
return nullptr;
}
void EmuThread::run() { void EmuThread::run() {
MicroProfileOnThreadCreate("EmuThread"); MicroProfileOnThreadCreate("EmuThread");
Core::Frontend::ScopeAcquireContext acquire_context{context}; // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution.
Core::System::GetInstance().GPU().Start();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
@ -111,162 +97,156 @@ void EmuThread::run() {
#endif #endif
} }
class GGLContext : public Core::Frontend::GraphicsContext { class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public: public:
explicit GGLContext(QOpenGLContext* shared_context) /// Create the original context that should be shared from
: context(new QOpenGLContext(shared_context->parent())), explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
surface(new QOffscreenSurface(nullptr)) { QSurfaceFormat format;
format.setVersion(4, 3);
// disable vsync for any shared contexts format.setProfile(QSurfaceFormat::CompatibilityProfile);
auto format = shared_context->format(); format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
// TODO: expose a setting for buffer value (ie default/single/double/triple)
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
format.setSwapInterval(0); format.setSwapInterval(0);
context->setShareContext(shared_context); context = std::make_unique<QOpenGLContext>();
context->setFormat(format); context->setFormat(format);
context->create(); if (!context->create()) {
surface->setParent(shared_context->parent()); LOG_ERROR(Frontend, "Unable to create main openGL context");
surface->setFormat(format); }
surface->create(); }
/// Create the shared contexts for rendering and presentation
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
// disable vsync for any shared contexts
auto format = share_context->format();
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
context->setFormat(format);
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create shared openGL context");
}
if (!main_surface) {
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
offscreen_surface->setFormat(format);
offscreen_surface->create();
surface = offscreen_surface.get();
} else {
surface = main_surface;
}
}
~OpenGLSharedContext() {
DoneCurrent();
}
void SwapBuffers() override {
context->swapBuffers(surface);
} }
void MakeCurrent() override { void MakeCurrent() override {
context->makeCurrent(surface); if (is_current) {
return;
}
is_current = context->makeCurrent(surface);
} }
void DoneCurrent() override { void DoneCurrent() override {
if (!is_current) {
return;
}
context->doneCurrent(); context->doneCurrent();
is_current = false;
}
QOpenGLContext* GetShareContext() {
return context.get();
}
const QOpenGLContext* GetShareContext() const {
return context.get();
} }
private: private:
QOpenGLContext* context; // Avoid using Qt parent system here since we might move the QObjects to new threads
QOffscreenSurface* surface; // As a note, this means we should avoid using slots/signals with the objects too
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
bool is_current = false;
}; };
class ChildRenderWindow : public QWindow { class DummyContext : public Core::Frontend::GraphicsContext {};
class RenderWidget : public QWidget {
public: public:
ChildRenderWindow(QWindow* parent, QWidget* event_handler) explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
: QWindow{parent}, event_handler{event_handler} {} setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
}
virtual ~ChildRenderWindow() = default; virtual ~RenderWidget() = default;
virtual void Present() = 0; /// Called on the UI thread when this Widget is ready to draw
/// Dervied classes can override this to draw the latest frame.
virtual void Present() {}
protected: void paintEvent(QPaintEvent* event) override {
bool event(QEvent* event) override {
switch (event->type()) {
case QEvent::UpdateRequest:
Present(); Present();
return true; update();
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::FocusAboutToChange:
case QEvent::Enter:
case QEvent::Leave:
case QEvent::Wheel:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::InputMethodQuery:
case QEvent::TouchCancel:
return QCoreApplication::sendEvent(event_handler, event);
case QEvent::Drop:
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
return true;
case QEvent::DragResponse:
case QEvent::DragEnter:
case QEvent::DragLeave:
case QEvent::DragMove:
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
return true;
default:
return QWindow::event(event);
}
} }
void exposeEvent(QExposeEvent* event) override { QPaintEngine* paintEngine() const override {
QWindow::requestUpdate(); return nullptr;
QWindow::exposeEvent(event);
} }
private: private:
QWidget* event_handler{}; GRenderWindow* render_window;
}; };
class OpenGLWindow final : public ChildRenderWindow { class OpenGLRenderWidget : public RenderWidget {
public: public:
OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
: ChildRenderWindow{parent, event_handler}, windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
context(new QOpenGLContext(shared_context->parent())) {
// disable vsync for any shared contexts
auto format = shared_context->format();
format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
this->setFormat(format);
context->setShareContext(shared_context);
context->setScreen(this->screen());
context->setFormat(format);
context->create();
setSurfaceType(QWindow::OpenGLSurface);
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
} }
~OpenGLWindow() override { void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) {
context->doneCurrent(); context = std::move(context_);
} }
void Present() override { void Present() override {
if (!isExposed()) { if (!isVisible()) {
return; return;
} }
context->makeCurrent(this); context->MakeCurrent();
Core::System::GetInstance().Renderer().TryPresent(100); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
context->swapBuffers(this); if (Core::System::GetInstance().Renderer().TryPresent(100)) {
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); context->SwapBuffers();
f->glFinish(); glFinish();
QWindow::requestUpdate(); }
} }
private: private:
QOpenGLContext* context{}; std::unique_ptr<Core::Frontend::GraphicsContext> context{};
}; };
#ifdef HAS_VULKAN #ifdef HAS_VULKAN
class VulkanWindow final : public ChildRenderWindow { class VulkanRenderWidget : public RenderWidget {
public: public:
VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
: ChildRenderWindow{parent, event_handler} { : RenderWidget(parent) {
setSurfaceType(QSurface::SurfaceType::VulkanSurface); windowHandle()->setSurfaceType(QWindow::VulkanSurface);
setVulkanInstance(instance); windowHandle()->setVulkanInstance(instance);
} }
~VulkanWindow() override = default;
void Present() override {
// TODO(bunnei): ImplementMe
}
private:
QWidget* event_handler{};
}; };
#endif #endif
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
: QWidget(parent_), emu_thread(emu_thread) { : QWidget(parent_), emu_thread(emu_thread) {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name), .arg(QString::fromUtf8(Common::g_build_name),
@ -278,26 +258,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
setLayout(layout); setLayout(layout);
InputCommon::Init(); InputCommon::Init();
GMainWindow* parent = GetMainWindow(); connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
} }
GRenderWindow::~GRenderWindow() { GRenderWindow::~GRenderWindow() {
InputCommon::Shutdown(); InputCommon::Shutdown();
} }
void GRenderWindow::MakeCurrent() {
if (core_context) {
core_context->MakeCurrent();
}
}
void GRenderWindow::DoneCurrent() {
if (core_context) {
core_context->DoneCurrent();
}
}
void GRenderWindow::PollEvents() { void GRenderWindow::PollEvents() {
if (!first_frame) { if (!first_frame) {
first_frame = true; first_frame = true;
@ -309,21 +276,6 @@ bool GRenderWindow::IsShown() const {
return !isMinimized(); return !isMinimized();
} }
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
#ifdef HAS_VULKAN
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
const VkInstance instance_copy = vk_instance->vkInstance();
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
#else
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
#endif
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
// //
// Older versions get the window size (density independent pixels), // Older versions get the window size (density independent pixels),
@ -367,7 +319,7 @@ qreal GRenderWindow::windowPixelRatio() const {
return devicePixelRatio(); return devicePixelRatio();
} }
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
const qreal pixel_ratio = windowPixelRatio(); const qreal pixel_ratio = windowPixelRatio();
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
@ -387,8 +339,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
} }
void GRenderWindow::mousePressEvent(QMouseEvent* event) { void GRenderWindow::mousePressEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem) // touch input is handled in TouchBeginEvent
return; // touch input is handled in TouchBeginEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
auto pos = event->pos(); auto pos = event->pos();
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
@ -400,8 +354,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
} }
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem) // touch input is handled in TouchUpdateEvent
return; // touch input is handled in TouchUpdateEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
auto pos = event->pos(); auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos); const auto [x, y] = ScaleTouch(pos);
@ -410,13 +366,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
} }
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem) // touch input is handled in TouchEndEvent
return; // touch input is handled in TouchEndEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
if (event->button() == Qt::LeftButton) if (event->button() == Qt::LeftButton) {
this->TouchReleased(); this->TouchReleased();
else if (event->button() == Qt::RightButton) } else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->EndTilt(); InputCommon::GetMotionEmu()->EndTilt();
}
} }
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
@ -474,9 +433,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); auto c = static_cast<OpenGLSharedContext*>(main_context.get());
// Bind the shared contexts to the main surface in case the backend wants to take over
// presentation
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
child_widget->windowHandle());
} }
return {}; return std::make_unique<DummyContext>();
} }
bool GRenderWindow::InitRenderTarget() { bool GRenderWindow::InitRenderTarget() {
@ -497,14 +460,11 @@ bool GRenderWindow::InitRenderTarget() {
break; break;
} }
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
// Reset minimum required size to avoid resizing issues on the main window after restarting. // Reset minimum required size to avoid resizing issues on the main window after restarting.
setMinimumSize(1, 1); setMinimumSize(1, 1);
// Show causes the window to actually be created and the gl context as well, but we don't want
// the widget to be shown yet, so immediately hide it.
show();
hide();
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
@ -523,9 +483,10 @@ bool GRenderWindow::InitRenderTarget() {
void GRenderWindow::ReleaseRenderTarget() { void GRenderWindow::ReleaseRenderTarget() {
if (child_widget) { if (child_widget) {
layout()->removeWidget(child_widget); layout()->removeWidget(child_widget);
delete child_widget; child_widget->deleteLater();
child_widget = nullptr; child_widget = nullptr;
} }
main_context.reset();
} }
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@ -557,24 +518,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
bool GRenderWindow::InitializeOpenGL() { bool GRenderWindow::InitializeOpenGL() {
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose // WA_DontShowOnScreen, WA_DeleteOnClose
QSurfaceFormat fmt; auto child = new OpenGLRenderWidget(this);
fmt.setVersion(4, 3); child_widget = child;
fmt.setProfile(QSurfaceFormat::CompatibilityProfile); child_widget->windowHandle()->create();
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
// TODO: expose a setting for buffer value (ie default/single/double/triple) main_context = context;
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); child->SetContext(
fmt.setSwapInterval(0); std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
QSurfaceFormat::setDefaultFormat(fmt);
GMainWindow* parent = GetMainWindow();
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
child_window->create();
child_widget = createWindowContainer(child_window, this);
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
core_context = CreateSharedContext();
return true; return true;
} }
@ -604,13 +554,10 @@ bool GRenderWindow::InitializeVulkan() {
return false; return false;
} }
GMainWindow* parent = GetMainWindow(); auto child = new VulkanRenderWidget(this, vk_instance.get());
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; child_widget = child;
child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); child_widget->windowHandle()->create();
child_window->create(); main_context = std::make_unique<DummyContext>();
child_widget = createWindowContainer(child_window, this);
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
return true; return true;
#else #else
@ -620,8 +567,24 @@ bool GRenderWindow::InitializeVulkan() {
#endif #endif
} }
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
#ifdef HAS_VULKAN
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
const VkInstance instance_copy = vk_instance->vkInstance();
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle());
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
#else
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
#endif
}
bool GRenderWindow::LoadOpenGL() { bool GRenderWindow::LoadOpenGL() {
Core::Frontend::ScopeAcquireContext acquire_context{*this}; auto context = CreateSharedContext();
auto scope = context->Acquire();
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
tr("Your GPU may not support OpenGL 4.3, or you do not have the " tr("Your GPU may not support OpenGL 4.3, or you do not have the "

View file

@ -18,12 +18,10 @@
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
class GRenderWindow; class GRenderWindow;
class GMainWindow;
class QKeyEvent; class QKeyEvent;
class QScreen;
class QTouchEvent; class QTouchEvent;
class QStringList; class QStringList;
class QSurface;
class QOpenGLContext;
#ifdef HAS_VULKAN #ifdef HAS_VULKAN
class QVulkanInstance; class QVulkanInstance;
#endif #endif
@ -36,7 +34,7 @@ class EmuThread final : public QThread {
Q_OBJECT Q_OBJECT
public: public:
explicit EmuThread(GRenderWindow& window); explicit EmuThread();
~EmuThread() override; ~EmuThread() override;
/** /**
@ -90,12 +88,6 @@ private:
std::mutex running_mutex; std::mutex running_mutex;
std::condition_variable running_cv; std::condition_variable running_cv;
/// Only used in asynchronous GPU mode
std::unique_ptr<Core::Frontend::GraphicsContext> shared_context;
/// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode
Core::Frontend::GraphicsContext& context;
signals: signals:
/** /**
* Emitted when the CPU has halted execution * Emitted when the CPU has halted execution
@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT Q_OBJECT
public: public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread); GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
~GRenderWindow() override; ~GRenderWindow() override;
// EmuWindow implementation. // EmuWindow implementation.
void MakeCurrent() override;
void DoneCurrent() override;
void PollEvents() override; void PollEvents() override;
bool IsShown() const override; bool IsShown() const override;
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
@ -165,6 +155,8 @@ public:
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
public slots: public slots:
void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping(); void OnEmulationStopping();
@ -176,7 +168,6 @@ signals:
void FirstFrameDisplayed(); void FirstFrameDisplayed();
private: private:
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event); void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent(); void TouchEndEvent();
@ -190,7 +181,10 @@ private:
EmuThread* emu_thread; EmuThread* emu_thread;
std::unique_ptr<GraphicsContext> core_context; // Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but
// should instead be shared from
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
#ifdef HAS_VULKAN #ifdef HAS_VULKAN
std::unique_ptr<QVulkanInstance> vk_instance; std::unique_ptr<QVulkanInstance> vk_instance;
@ -201,12 +195,6 @@ private:
QByteArray geometry; QByteArray geometry;
/// Native window handle that backs this presentation widget
QWindow* child_window = nullptr;
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
/// put the child_window into a widget then add it to the layout. This child_widget can be
/// parented to GRenderWindow and use Qt's lifetime system
QWidget* child_widget = nullptr; QWidget* child_widget = nullptr;
bool first_frame = false; bool first_frame = false;

View file

@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) {
return; return;
// Create and start the emulation thread // Create and start the emulation thread
emu_thread = std::make_unique<EmuThread>(*render_window); emu_thread = std::make_unique<EmuThread>();
emit EmulationStarting(emu_thread.get()); emit EmulationStarting(emu_thread.get());
emu_thread->start(); emu_thread->start();
@ -2378,7 +2378,6 @@ int main(int argc, char* argv[]) {
// Enables the core to make the qt created contexts current on std::threads // Enables the core to make the qt created contexts current on std::threads
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QApplication app(argc, argv); QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when // Qt changes the locale and causes issues in float conversion using std::to_string() when

View file

@ -37,16 +37,24 @@ public:
} }
void MakeCurrent() override { void MakeCurrent() override {
SDL_GL_MakeCurrent(window, context); if (is_current) {
return;
}
is_current = SDL_GL_MakeCurrent(window, context) == 0;
} }
void DoneCurrent() override { void DoneCurrent() override {
if (!is_current) {
return;
}
SDL_GL_MakeCurrent(window, nullptr); SDL_GL_MakeCurrent(window, nullptr);
is_current = false;
} }
private: private:
SDL_Window* window; SDL_Window* window;
SDL_GLContext context; SDL_GLContext context;
bool is_current = false;
}; };
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
@ -148,14 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
SDL_GL_DeleteContext(window_context); SDL_GL_DeleteContext(window_context);
} }
void EmuWindow_SDL2_GL::MakeCurrent() {
core_context->MakeCurrent();
}
void EmuWindow_SDL2_GL::DoneCurrent() {
core_context->DoneCurrent();
}
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const { void* surface) const {
// Should not have been called from OpenGL // Should not have been called from OpenGL

View file

@ -13,8 +13,6 @@ public:
explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
~EmuWindow_SDL2_GL(); ~EmuWindow_SDL2_GL();
void MakeCurrent() override;
void DoneCurrent() override;
void Present() override; void Present() override;
/// Ignored in OpenGL /// Ignored in OpenGL

View file

@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
vkDestroyInstance(vk_instance, nullptr); vkDestroyInstance(vk_instance, nullptr);
} }
void EmuWindow_SDL2_VK::MakeCurrent() {
// Unused on Vulkan
}
void EmuWindow_SDL2_VK::DoneCurrent() {
// Unused on Vulkan
}
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const { void* surface) const {
const auto instance_proc_addr = vkGetInstanceProcAddr; const auto instance_proc_addr = vkGetInstanceProcAddr;

View file

@ -13,8 +13,6 @@ public:
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
~EmuWindow_SDL2_VK(); ~EmuWindow_SDL2_VK();
void MakeCurrent() override;
void DoneCurrent() override;
void Present() override; void Present() override;
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override; void* surface) const override;

View file

@ -230,17 +230,10 @@ int main(int argc, char** argv) {
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
system.Renderer().Rasterizer().LoadDiskResources(); // Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
// Acquire render context for duration of the thread if this is the rendering thread system.Renderer().Rasterizer().LoadDiskResources();
if (!Settings::values.use_asynchronous_gpu_emulation) {
emu_window->MakeCurrent();
}
SCOPE_EXIT({
if (!Settings::values.use_asynchronous_gpu_emulation) {
emu_window->DoneCurrent();
}
});
std::thread render_thread([&emu_window] { emu_window->Present(); }); std::thread render_thread([&emu_window] { emu_window->Present(); });
while (emu_window->IsOpen()) { while (emu_window->IsOpen()) {

View file

@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc); Common::g_scm_branch, Common::g_scm_desc);
Settings::LogSettings(); Settings::LogSettings();
DoneCurrent();
} }
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
@ -114,14 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
void EmuWindow_SDL2_Hide::PollEvents() {} void EmuWindow_SDL2_Hide::PollEvents() {}
void EmuWindow_SDL2_Hide::MakeCurrent() {
SDL_GL_MakeCurrent(render_window, gl_context);
}
void EmuWindow_SDL2_Hide::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
bool EmuWindow_SDL2_Hide::IsShown() const { bool EmuWindow_SDL2_Hide::IsShown() const {
return false; return false;
} }
@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const {
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
UNREACHABLE(); UNREACHABLE();
} }
class SDLGLContext : public Core::Frontend::GraphicsContext {
public:
explicit SDLGLContext() {
// create a hidden window to make the shared context against
window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
context = SDL_GL_CreateContext(window);
}
~SDLGLContext() {
DoneCurrent();
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
}
void MakeCurrent() override {
SDL_GL_MakeCurrent(window, context);
}
void DoneCurrent() override {
SDL_GL_MakeCurrent(window, nullptr);
}
private:
SDL_Window* window;
SDL_GLContext context;
};
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const {
return std::make_unique<SDLGLContext>();
}

View file

@ -16,12 +16,6 @@ public:
/// Polls window events /// Polls window events
void PollEvents() override; void PollEvents() override;
/// Makes the graphics context current for the caller thread
void MakeCurrent() override;
/// Releases the GL context from the caller thread
void DoneCurrent() override;
/// Whether the screen is being shown or not. /// Whether the screen is being shown or not.
bool IsShown() const override; bool IsShown() const override;
@ -29,8 +23,7 @@ public:
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override; void* surface) const override;
/// Whether the window is still open, and a close request hasn't yet been sent std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
bool IsOpen() const;
private: private:
/// Whether the GPU and driver supports the OpenGL extension required /// Whether the GPU and driver supports the OpenGL extension required

View file

@ -164,11 +164,6 @@ int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
if (!Settings::values.use_multi_core) {
// Single core mode must acquire OpenGL context for entire emulation session
emu_window->MakeCurrent();
}
bool finished = false; bool finished = false;
int return_value = 0; int return_value = 0;
const auto callback = [&finished, const auto callback = [&finished,
@ -257,6 +252,7 @@ int main(int argc, char** argv) {
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
system.GPU().Start();
system.Renderer().Rasterizer().LoadDiskResources(); system.Renderer().Rasterizer().LoadDiskResources();
while (!finished) { while (!finished) {