diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b31a0328c1..29a267957b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/core.cpp b/src/core/core.cpp index 2185081262..6cc4a08121 100644 --- a/src/core/core.cpp +++ b/src/core/core.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(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 app_loader; - std::unique_ptr renderer; std::unique_ptr gpu_core; std::unique_ptr 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() { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63c..bb283d8444 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -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 CreateSharedContext() const { - return nullptr; - } + virtual std::unique_ptr CreateSharedContext() const = 0; /// Returns if window is shown (not minimized) virtual bool IsShown() const = 0; diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp deleted file mode 100644 index 878c3157c2..0000000000 --- a/src/core/frontend/scope_acquire_context.cpp +++ /dev/null @@ -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 diff --git a/src/core/frontend/scope_acquire_context.h b/src/core/frontend/scope_acquire_context.h deleted file mode 100644 index 7a65c06231..0000000000 --- a/src/core/frontend/scope_acquire_context.h +++ /dev/null @@ -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 diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e8f763ce92..8acf2eda25 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -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&& renderer_, bool is_async) + : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { + auto& rasterizer{renderer->Rasterizer()}; memory_manager = std::make_unique(system, rasterizer); dma_pusher = std::make_unique(*this); maxwell_3d = std::make_unique(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 diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 64acb17dfa..ced9d7e286 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -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&& 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 dma_pusher; Core::System& system; - VideoCore::RendererBase& renderer; + std::unique_ptr renderer; private: std::unique_ptr memory_manager; diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 04222d0607..925be8d7b0 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp @@ -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&& renderer_, + std::unique_ptr&& 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) { diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 1241ade1d7..265c62758c 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h @@ -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&& renderer, + std::unique_ptr&& context); ~GPUAsynch() override; void Start() override; @@ -32,6 +37,8 @@ protected: private: GPUThread::ThreadManager gpu_thread; + std::unique_ptr cpu_context; + std::unique_ptr gpu_context; }; } // namespace VideoCommon diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index d482210772..bd5278a5c3 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp @@ -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&& renderer, + std::unique_ptr&& 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 diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index c71baee89e..866a94c8c2 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h @@ -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&& renderer, + std::unique_ptr&& 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 context; }; } // namespace VideoCommon diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b1088af3d2..270c7ae0d6 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -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) { diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 882e2d9c78..be36c580eb 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,7 +10,6 @@ #include #include #include - #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); diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5ec99a1260..1d85219b65 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -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: // ------------------------ diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index e3d31c3eb9..8f59e04425 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -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) { diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index fca5e3ec06..6f08803c12 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -7,9 +7,7 @@ #include #include #include - #include - #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 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()} {} +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(); + 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() { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 33073ce5b0..50b6476612 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -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 frame_mailbox; + + bool has_debug_tool = false; }; } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 42bb014187..6953aaafe0 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -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() { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 3da08d2e43..d14384e792 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -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 CreateDebugCallback( diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index a5f81a8a0c..fd9fec0186 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -15,13 +15,13 @@ #endif #include "video_core/video_core.h" -namespace VideoCore { - -std::unique_ptr CreateRenderer(Core::Frontend::EmuWindow& emu_window, - Core::System& system) { +namespace { +std::unique_ptr 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(emu_window, system); + return std::make_unique(emu_window, system, context); #ifdef HAS_VULKAN case Settings::RendererBackend::Vulkan: return std::make_unique(emu_window, system); @@ -30,13 +30,23 @@ std::unique_ptr CreateRenderer(Core::Frontend::EmuWindow& emu_wind return nullptr; } } +} // namespace -std::unique_ptr CreateGPU(Core::System& system) { - if (Settings::values.use_asynchronous_gpu_emulation) { - return std::make_unique(system, system.Renderer()); +namespace VideoCore { + +std::unique_ptr 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(system, system.Renderer()); + if (Settings::values.use_asynchronous_gpu_emulation) { + return std::make_unique(system, std::move(renderer), + std::move(context)); + } + return std::make_unique(system, std::move(renderer), std::move(context)); } u16 GetResolutionScaleFactor(const RendererBase& renderer) { diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index b8e0ac3728..f5c27125d7 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -22,17 +22,8 @@ namespace VideoCore { 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 CreateRenderer(Core::Frontend::EmuWindow& emu_window, - Core::System& system); - /// Creates an emulated GPU instance using the given system context. -std::unique_ptr CreateGPU(Core::System& system); +std::unique_ptr CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); u16 GetResolutionScaleFactor(const RendererBase& renderer); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88a..d120ee8187 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -10,9 +10,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -29,7 +26,6 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" -#include "core/frontend/scope_acquire_context.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" @@ -39,27 +35,13 @@ #include "yuzu/bootmanager.h" #include "yuzu/main.h" -EmuThread::EmuThread(GRenderWindow& window) - : 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(w)) { - return main; - } - } - return nullptr; -} - void EmuThread::run() { MicroProfileOnThreadCreate("EmuThread"); - Core::Frontend::ScopeAcquireContext acquire_context{context}; - emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( @@ -69,6 +51,10 @@ void EmuThread::run() { emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + // Main process has been loaded. Make the context current to this thread and begin GPU and CPU + // execution. + Core::System::GetInstance().GPU().Start(); + // Holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the // next execution step @@ -111,162 +97,195 @@ void EmuThread::run() { #endif } -class GGLContext : public Core::Frontend::GraphicsContext { +class OpenGLSharedContext : public Core::Frontend::GraphicsContext { public: - explicit GGLContext(QOpenGLContext* shared_context) - : context(new QOpenGLContext(shared_context->parent())), - surface(new QOffscreenSurface(nullptr)) { - - // disable vsync for any shared contexts - auto format = shared_context->format(); + /// Create the original context that should be shared from + explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { + QSurfaceFormat format; + format.setVersion(4, 3); + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); + // TODO: expose a setting for buffer value (ie default/single/double/triple) + format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); format.setSwapInterval(0); - context->setShareContext(shared_context); + context = std::make_unique(); context->setFormat(format); - context->create(); - surface->setParent(shared_context->parent()); - surface->setFormat(format); - surface->create(); + if (!context->create()) { + LOG_ERROR(Frontend, "Unable to create main openGL context"); + } + } + + /// 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(); + 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(nullptr); + offscreen_surface->setFormat(format); + offscreen_surface->create(); + surface = offscreen_surface.get(); + } else { + surface = main_surface; + } + } + + ~OpenGLSharedContext() { + context->doneCurrent(); + } + + void SwapBuffers() override { + context->swapBuffers(surface); } void MakeCurrent() override { + if (is_current) { + return; + } context->makeCurrent(surface); } void DoneCurrent() override { context->doneCurrent(); + is_current = false; + } + + QOpenGLContext* GetShareContext() const { + return context.get(); } private: - QOpenGLContext* context; - QOffscreenSurface* surface; + // Avoid using Qt parent system here since we might move the QObjects to new threads + // As a note, this means we should avoid using slots/signals with the objects too + std::unique_ptr context; + std::unique_ptr offscreen_surface{}; + QSurface* surface; + bool is_current = false; }; -class ChildRenderWindow : public QWindow { +class DummyContext : public Core::Frontend::GraphicsContext {}; + +class RenderWidget : public QWidget { public: - ChildRenderWindow(QWindow* parent, QWidget* event_handler) - : QWindow{parent}, event_handler{event_handler} {} + RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_PaintOnScreen); + } - virtual ~ChildRenderWindow() = default; + virtual ~RenderWidget() = default; - virtual void Present() = 0; + virtual void Present() {} -protected: - bool event(QEvent* event) override { - switch (event->type()) { - case QEvent::UpdateRequest: - Present(); - return true; - 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(event)); - return true; - case QEvent::DragResponse: - case QEvent::DragEnter: - case QEvent::DragLeave: - case QEvent::DragMove: - GetMainWindow()->AcceptDropEvent(static_cast(event)); - return true; - default: - return QWindow::event(event); + void paintEvent(QPaintEvent* event) override { + Present(); + update(); + } + + void resizeEvent(QResizeEvent* ev) override { + render_window->resize(ev->size()); + render_window->OnFramebufferSizeChanged(); + } + + void keyPressEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->PressKey(event->key()); + } + + void keyReleaseEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->ReleaseKey(event->key()); + } + + void mousePressEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchBeginEvent + + const auto pos{event->pos()}; + if (event->button() == Qt::LeftButton) { + const auto [x, y] = render_window->ScaleTouch(pos); + render_window->TouchPressed(x, y); + } else if (event->button() == Qt::RightButton) { + InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); } } - void exposeEvent(QExposeEvent* event) override { - QWindow::requestUpdate(); - QWindow::exposeEvent(event); + void mouseMoveEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchUpdateEvent + + const auto pos{event->pos()}; + const auto [x, y] = render_window->ScaleTouch(pos); + render_window->TouchMoved(x, y); + InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); + } + + void mouseReleaseEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchEndEvent + + if (event->button() == Qt::LeftButton) + render_window->TouchReleased(); + else if (event->button() == Qt::RightButton) + InputCommon::GetMotionEmu()->EndTilt(); + } + + std::pair GetSize() const { + return std::make_pair(width(), height()); + } + + QPaintEngine* paintEngine() const override { + return nullptr; } private: - QWidget* event_handler{}; + GRenderWindow* render_window; }; -class OpenGLWindow final : public ChildRenderWindow { +class OpenGLRenderWidget : public RenderWidget { public: - OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) - : ChildRenderWindow{parent, event_handler}, - 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 + explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::OpenGLSurface); } - ~OpenGLWindow() override { - context->doneCurrent(); + void SetContext(std::unique_ptr&& context_) { + context = std::move(context_); } void Present() override { - if (!isExposed()) { + if (!isVisible()) { return; } - context->makeCurrent(this); - Core::System::GetInstance().Renderer().TryPresent(100); - context->swapBuffers(this); - auto f = context->versionFunctions(); - f->glFinish(); - QWindow::requestUpdate(); + context->MakeCurrent(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + if (Core::System::GetInstance().Renderer().TryPresent(100)) { + context->SwapBuffers(); + glFinish(); + } } private: - QOpenGLContext* context{}; + std::unique_ptr context{}; }; -#ifdef HAS_VULKAN -class VulkanWindow final : public ChildRenderWindow { +class VulkanRenderWidget : public RenderWidget { public: - VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) - : ChildRenderWindow{parent, event_handler} { - setSurfaceType(QSurface::SurfaceType::VulkanSurface); - setVulkanInstance(instance); + explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance) + : RenderWidget(parent) { + windowHandle()->setSurfaceType(QWindow::VulkanSurface); + windowHandle()->setVulkanInstance(instance); } - - ~VulkanWindow() override = default; - - void Present() override { - // TODO(bunnei): ImplementMe - } - -private: - QWidget* event_handler{}; }; -#endif -GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) +GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread) : QWidget(parent_), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), @@ -278,26 +297,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) setLayout(layout); InputCommon::Init(); - GMainWindow* parent = GetMainWindow(); - connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); + connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); } GRenderWindow::~GRenderWindow() { InputCommon::Shutdown(); } -void GRenderWindow::MakeCurrent() { - if (core_context) { - core_context->MakeCurrent(); - } -} - -void GRenderWindow::DoneCurrent() { - if (core_context) { - core_context->DoneCurrent(); - } -} - void GRenderWindow::PollEvents() { if (!first_frame) { first_frame = true; @@ -309,21 +315,6 @@ bool GRenderWindow::IsShown() const { 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). // // Older versions get the window size (density independent pixels), @@ -474,9 +465,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { std::unique_ptr GRenderWindow::CreateSharedContext() const { if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { - return std::make_unique(QOpenGLContext::globalShareContext()); + auto c = static_cast(main_context.get()); + // Bind the shared contexts to the main surface in case the backend wants to take over + // presentation + return std::make_unique(c->GetShareContext(), + child_widget->windowHandle()); } - return {}; + return std::make_unique(); } bool GRenderWindow::InitRenderTarget() { @@ -497,14 +492,11 @@ bool GRenderWindow::InitRenderTarget() { 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. 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); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); @@ -523,9 +515,10 @@ bool GRenderWindow::InitRenderTarget() { void GRenderWindow::ReleaseRenderTarget() { if (child_widget) { layout()->removeWidget(child_widget); - delete child_widget; + child_widget->deleteLater(); child_widget = nullptr; } + main_context.reset(); } void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { @@ -557,24 +550,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair minimal bool GRenderWindow::InitializeOpenGL() { // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // WA_DontShowOnScreen, WA_DeleteOnClose - QSurfaceFormat fmt; - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); - // TODO: expose a setting for buffer value (ie default/single/double/triple) - fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); - fmt.setSwapInterval(0); - 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(); + auto child = new OpenGLRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); + auto context = std::make_shared(child->windowHandle()); + main_context = context; + child->SetContext( + std::make_unique(context->GetShareContext(), child->windowHandle())); return true; } @@ -604,13 +586,10 @@ bool GRenderWindow::InitializeVulkan() { return false; } - GMainWindow* parent = GetMainWindow(); - QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; - child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); - child_window->create(); - child_widget = createWindowContainer(child_window, this); - child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); - layout()->addWidget(child_widget); + auto child = new VulkanRenderWidget(this, vk_instance.get()); + child_widget = child; + child_widget->windowHandle()->create(); + main_context = std::make_unique(); return true; #else @@ -620,8 +599,24 @@ bool GRenderWindow::InitializeVulkan() { #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() { - Core::Frontend::ScopeAcquireContext acquire_context{*this}; + auto context = CreateSharedContext(); + auto scope = context->Acquire(); if (!gladLoadGL()) { 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 " diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 79b0303044..3739ec7ed0 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -7,23 +7,20 @@ #include #include #include - +#include #include #include #include #include - #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" class GRenderWindow; +class GMainWindow; class QKeyEvent; -class QScreen; class QTouchEvent; class QStringList; -class QSurface; -class QOpenGLContext; #ifdef HAS_VULKAN class QVulkanInstance; #endif @@ -36,7 +33,7 @@ class EmuThread final : public QThread { Q_OBJECT public: - explicit EmuThread(GRenderWindow& window); + explicit EmuThread(); ~EmuThread() override; /** @@ -87,14 +84,8 @@ private: bool exec_step = false; bool running = false; std::atomic_bool stop_run{false}; - std::mutex running_mutex; - std::condition_variable running_cv; - - /// Only used in asynchronous GPU mode - std::unique_ptr shared_context; - - /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode - Core::Frontend::GraphicsContext& context; + std::mutex running_mutex = {}; + std::condition_variable running_cv = {}; signals: /** @@ -124,12 +115,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { Q_OBJECT public: - GRenderWindow(QWidget* parent, EmuThread* emu_thread); + GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); ~GRenderWindow() override; // EmuWindow implementation. - void MakeCurrent() override; - void DoneCurrent() override; void PollEvents() override; bool IsShown() const override; void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, @@ -165,6 +154,8 @@ public: void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); + std::pair ScaleTouch(const QPointF pos) const; + public slots: void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStopping(); @@ -176,7 +167,6 @@ signals: void FirstFrameDisplayed(); private: - std::pair ScaleTouch(QPointF pos) const; void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); @@ -190,7 +180,10 @@ private: EmuThread* emu_thread; - std::unique_ptr 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 main_context; #ifdef HAS_VULKAN std::unique_ptr vk_instance; @@ -201,12 +194,6 @@ private: 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; bool first_frame = false; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 47615adfea..d7684e2418 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) { return; // Create and start the emulation thread - emu_thread = std::make_unique(*render_window); + emu_thread = std::make_unique(); emit EmulationStarting(emu_thread.get()); emu_thread->start(); @@ -2375,7 +2375,6 @@ int main(int argc, char* argv[]) { // Enables the core to make the qt created contexts current on std::threads QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QApplication app(argc, argv); // Qt changes the locale and causes issues in float conversion using std::to_string() when diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index c0d373477c..ee61179a09 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -148,14 +148,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { 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* surface) const { // Should not have been called from OpenGL diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index b80669ff05..e092021d79 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h @@ -13,8 +13,6 @@ public: explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); ~EmuWindow_SDL2_GL(); - void MakeCurrent() override; - void DoneCurrent() override; void Present() override; /// Ignored in OpenGL diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index abcc581651..46d053f047 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { 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* surface) const { const auto instance_proc_addr = vkGetInstanceProcAddr; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index 1eb8c0868e..3dd1f3f619 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -13,8 +13,6 @@ public: explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); ~EmuWindow_SDL2_VK(); - void MakeCurrent() override; - void DoneCurrent() override; void Present() override; void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, void* surface) const override; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index babf4c3a49..e5db7d819a 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -232,15 +232,8 @@ int main(int argc, char** argv) { system.Renderer().Rasterizer().LoadDiskResources(); - // Acquire render context for duration of the thread if this is the rendering thread - if (!Settings::values.use_asynchronous_gpu_emulation) { - emu_window->MakeCurrent(); - } - SCOPE_EXIT({ - if (!Settings::values.use_asynchronous_gpu_emulation) { - emu_window->DoneCurrent(); - } - }); + // Core is loaded, start the GPU (makes the GPU contexts current to this thread) + system.GPU().Start(); std::thread render_thread([&emu_window] { emu_window->Present(); }); while (emu_window->IsOpen()) {