diff options
29 files changed, 361 insertions, 418 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b31a0328c..29a267957 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 218508126..6cc4a0812 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<Core::Hardware::InterruptManager>(system); -        gpu_core = VideoCore::CreateGPU(system); -        renderer->Rasterizer().SetupDirtyFlags(); +        gpu_core = VideoCore::CreateGPU(emu_window, system); +        gpu_core->Renderer().Rasterizer().SetupDirtyFlags();          is_powered_on = true;          exit_lock = false; @@ -186,7 +181,6 @@ struct System::Impl {      ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,                        const std::string& filepath) { -        Core::Frontend::ScopeAcquireContext acquire_context{emu_window};          app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));          if (!app_loader) { @@ -216,10 +210,6 @@ struct System::Impl {          AddGlueRegistrationForProcess(*app_loader, *main_process);          kernel.MakeCurrentProcess(main_process.get()); -        // Main process has been loaded and been made current. -        // Begin GPU and CPU execution. -        gpu_core->Start(); -          // Initialize cheat engine          if (cheat_engine) {              cheat_engine->Initialize(); @@ -277,7 +267,6 @@ struct System::Impl {          }          // Shutdown emulation session -        renderer.reset();          GDBStub::Shutdown();          Service::Shutdown();          service_manager.reset(); @@ -353,7 +342,6 @@ struct System::Impl {      Service::FileSystem::FileSystemController fs_controller;      /// AppLoader used to load the current executing application      std::unique_ptr<Loader::AppLoader> app_loader; -    std::unique_ptr<VideoCore::RendererBase> renderer;      std::unique_ptr<Tegra::GPU> gpu_core;      std::unique_ptr<Hardware::InterruptManager> interrupt_manager;      Memory::Memory memory; @@ -536,11 +524,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {  }  VideoCore::RendererBase& System::Renderer() { -    return *impl->renderer; +    return impl->gpu_core->Renderer();  }  const VideoCore::RendererBase& System::Renderer() const { -    return *impl->renderer; +    return impl->gpu_core->Renderer();  }  Kernel::KernelCore& System::Kernel() { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63..bb283d844 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<GraphicsContext> CreateSharedContext() const { -        return nullptr; -    } +    virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;      /// Returns if window is shown (not minimized)      virtual bool IsShown() const = 0; diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp deleted file mode 100644 index 878c3157c..000000000 --- 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 7a65c0623..000000000 --- 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 e8f763ce9..8acf2eda2 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<VideoCore::RendererBase>&& renderer_, bool is_async) +    : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { +    auto& rasterizer{renderer->Rasterizer()};      memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);      dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);      maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); @@ -137,7 +139,7 @@ u64 GPU::GetTicks() const {  }  void GPU::FlushCommands() { -    renderer.Rasterizer().FlushCommands(); +    renderer->Rasterizer().FlushCommands();  }  // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 64acb17df..ced9d7e28 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<VideoCore::RendererBase>&& renderer, +                 bool is_async);      virtual ~GPU(); @@ -174,6 +178,14 @@ public:      /// Returns a reference to the GPU DMA pusher.      Tegra::DmaPusher& DmaPusher(); +    VideoCore::RendererBase& Renderer() { +        return *renderer; +    } + +    const VideoCore::RendererBase& Renderer() const { +        return *renderer; +    } +      // Waits for the GPU to finish working      virtual void WaitIdle() const = 0; @@ -287,7 +299,7 @@ private:  protected:      std::unique_ptr<Tegra::DmaPusher> dma_pusher;      Core::System& system; -    VideoCore::RendererBase& renderer; +    std::unique_ptr<VideoCore::RendererBase> renderer;  private:      std::unique_ptr<Tegra::MemoryManager> memory_manager; diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 04222d060..925be8d7b 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<VideoCore::RendererBase>&& renderer_, +                     std::unique_ptr<Core::Frontend::GraphicsContext>&& context) +    : GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)), +      cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {}  GPUAsynch::~GPUAsynch() = default;  void GPUAsynch::Start() { -    gpu_thread.StartThread(renderer, *dma_pusher); +    cpu_context->MakeCurrent(); +    gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);  }  void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 1241ade1d..265c62758 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<VideoCore::RendererBase>&& renderer, +                       std::unique_ptr<Core::Frontend::GraphicsContext>&& context);      ~GPUAsynch() override;      void Start() override; @@ -32,6 +37,8 @@ protected:  private:      GPUThread::ThreadManager gpu_thread; +    std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; +    std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;  };  } // namespace VideoCommon diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index d48221077..bd5278a5c 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<VideoCore::RendererBase>&& renderer, +                   std::unique_ptr<Core::Frontend::GraphicsContext>&& context) +    : GPU(system, std::move(renderer), false), context{std::move(context)} {}  GPUSynch::~GPUSynch() = default; -void GPUSynch::Start() {} +void GPUSynch::Start() { +    context->MakeCurrent(); +}  void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {      dma_pusher->Push(std::move(entries)); @@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {  }  void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { -    renderer.SwapBuffers(framebuffer); +    renderer->SwapBuffers(framebuffer);  }  void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { -    renderer.Rasterizer().FlushRegion(addr, size); +    renderer->Rasterizer().FlushRegion(addr, size);  }  void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) { -    renderer.Rasterizer().InvalidateRegion(addr, size); +    renderer->Rasterizer().InvalidateRegion(addr, size);  }  void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { -    renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); +    renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);  }  } // namespace VideoCommon diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index c71baee89..866a94c8c 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<VideoCore::RendererBase>&& renderer, +                      std::unique_ptr<Core::Frontend::GraphicsContext>&& context);      ~GPUSynch() override;      void Start() override; @@ -29,6 +34,9 @@ public:  protected:      void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,                               [[maybe_unused]] u32 value) const override {} + +private: +    std::unique_ptr<Core::Frontend::GraphicsContext> context;  };  } // namespace VideoCommon diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b1088af3d..270c7ae0d 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 882e2d9c7..be36c580e 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,7 +10,6 @@  #include <optional>  #include <thread>  #include <variant> -  #include "common/threadsafe_queue.h"  #include "video_core/gpu.h" @@ -20,6 +19,9 @@ class DmaPusher;  } // namespace Tegra  namespace Core { +namespace Frontend { +class GraphicsContext; +}  class System;  } // namespace Core @@ -99,7 +101,8 @@ public:      ~ThreadManager();      /// Creates and starts the GPU thread. -    void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); +    void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, +                     Tegra::DmaPusher& dma_pusher);      /// Push GPU command entries to be processed      void SubmitList(Tegra::CommandList&& entries); diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5ec99a126..1d85219b6 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 e3d31c3eb..8f59e0442 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 fca5e3ec0..6f08803c1 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -7,9 +7,7 @@  #include <cstdlib>  #include <cstring>  #include <memory> -  #include <glad/glad.h> -  #include "common/assert.h"  #include "common/logging/log.h"  #include "common/microprofile.h" @@ -30,8 +28,6 @@ namespace OpenGL {  namespace { -// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have -// to wait on available presentation frames.  constexpr std::size_t SWAP_CHAIN_SIZE = 3;  struct Frame { @@ -214,7 +210,7 @@ public:      std::deque<Frame*> present_queue;      Frame* previous_frame{}; -    FrameMailbox() : has_debug_tool{HasDebugTool()} { +    FrameMailbox() {          for (auto& frame : swap_chain) {              free_queue.push(&frame);          } @@ -285,13 +281,9 @@ public:          std::unique_lock lock{swap_chain_lock};          present_queue.push_front(frame);          present_cv.notify_one(); - -        DebugNotifyNextFrame();      }      Frame* TryGetPresentFrame(int timeout_ms) { -        DebugWaitForNextFrame(); -          std::unique_lock lock{swap_chain_lock};          // wait for new entries in the present_queue          present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), @@ -317,38 +309,12 @@ public:          previous_frame = frame;          return frame;      } - -private: -    std::mutex debug_synch_mutex; -    std::condition_variable debug_synch_condition; -    std::atomic_int frame_for_debug{}; -    const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step - -    /// Signal that a new frame is available (called from GPU thread) -    void DebugNotifyNextFrame() { -        if (!has_debug_tool) { -            return; -        } -        frame_for_debug++; -        std::lock_guard lock{debug_synch_mutex}; -        debug_synch_condition.notify_one(); -    } - -    /// Wait for a new frame to be available (called from presentation thread) -    void DebugWaitForNextFrame() { -        if (!has_debug_tool) { -            return; -        } -        const int last_frame = frame_for_debug; -        std::unique_lock lock{debug_synch_mutex}; -        debug_synch_condition.wait(lock, -                                   [this, last_frame] { return frame_for_debug > last_frame; }); -    }  }; -RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) -    : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, -      frame_mailbox{std::make_unique<FrameMailbox>()} {} +RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, +                               Core::Frontend::GraphicsContext& context) +    : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, frame_mailbox{}, +      has_debug_tool{HasDebugTool()}, context{context} {}  RendererOpenGL::~RendererOpenGL() = default; @@ -356,8 +322,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12  MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));  void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { -    render_window.PollEvents(); -      if (!framebuffer) {          return;      } @@ -413,6 +377,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {          m_current_frame++;          rasterizer->TickFrame();      } + +    render_window.PollEvents(); +    if (has_debug_tool) { +        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +        Present(0); +        context.SwapBuffers(); +    }  }  void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { @@ -480,6 +451,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color  }  void RendererOpenGL::InitOpenGLObjects() { +    frame_mailbox = std::make_unique<FrameMailbox>(); +      glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,                   0.0f); @@ -692,12 +665,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  } -void RendererOpenGL::TryPresent(int timeout_ms) { +bool RendererOpenGL::TryPresent(int timeout_ms) { +    if (has_debug_tool) { +        LOG_DEBUG(Render_OpenGL, +                  "Skipping presentation because we are presenting on the main context"); +        return false; +    } +    return Present(timeout_ms); +} + +bool RendererOpenGL::Present(int timeout_ms) {      const auto& layout = render_window.GetFramebufferLayout();      auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);      if (!frame) {          LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); -        return; +        return false;      }      // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a @@ -725,6 +707,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {      glFlush();      glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +    return true;  }  void RendererOpenGL::RenderScreenshot() { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 33073ce5b..50b647661 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<FrameMailbox> 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 42bb01418..6953aaafe 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 3da08d2e4..d14384e79 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<vk::DebugUtilsMessengerEXT> CreateDebugCallback( diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index a5f81a8a0..fd9fec018 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<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, -                                             Core::System& system) { +namespace { +std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, +                                                        Core::System& system, +                                                        Core::Frontend::GraphicsContext& context) {      switch (Settings::values.renderer_backend) {      case Settings::RendererBackend::OpenGL: -        return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); +        return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);  #ifdef HAS_VULKAN      case Settings::RendererBackend::Vulkan:          return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); @@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind          return nullptr;      }  } +} // namespace -std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { -    if (Settings::values.use_asynchronous_gpu_emulation) { -        return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer()); +namespace VideoCore { + +std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { +    auto context = emu_window.CreateSharedContext(); +    const auto scope = context->Acquire(); +    auto renderer = CreateRenderer(emu_window, system, *context); +    if (!renderer->Init()) { +        return {};      } -    return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer()); +    if (Settings::values.use_asynchronous_gpu_emulation) { +        return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), +                                                        std::move(context)); +    } +    return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));  }  u16 GetResolutionScaleFactor(const RendererBase& renderer) { diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index b8e0ac372..f5c27125d 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<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, -                                             Core::System& system); -  /// 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); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88..d120ee818 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -10,9 +10,6 @@  #include <QMessageBox>  #include <QOffscreenSurface>  #include <QOpenGLContext> -#include <QOpenGLFunctions> -#include <QOpenGLFunctions_4_3_Core> -#include <QOpenGLWindow>  #include <QPainter>  #include <QScreen>  #include <QStringList> @@ -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<GMainWindow*>(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)) { +    /// 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 = std::make_unique<QOpenGLContext>(); +        context->setFormat(format); +        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 = shared_context->format(); -        format.setSwapInterval(0); +        auto format = share_context->format(); +        format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); -        context->setShareContext(shared_context); +        context = std::make_unique<QOpenGLContext>(); +        context->setShareContext(share_context);          context->setFormat(format); -        context->create(); -        surface->setParent(shared_context->parent()); -        surface->setFormat(format); -        surface->create(); +        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() { +        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<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: -    ChildRenderWindow(QWindow* parent, QWidget* event_handler) -        : QWindow{parent}, event_handler{event_handler} {} - -    virtual ~ChildRenderWindow() = default; - -    virtual void Present() = 0; - -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<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); -        } +    RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { +        setAttribute(Qt::WA_NativeWindow); +        setAttribute(Qt::WA_PaintOnScreen);      } -    void exposeEvent(QExposeEvent* event) override { -        QWindow::requestUpdate(); -        QWindow::exposeEvent(event); +    virtual ~RenderWidget() = default; + +    virtual void Present() {} + +    void paintEvent(QPaintEvent* event) override { +        Present(); +        update();      } -private: -    QWidget* event_handler{}; -}; +    void resizeEvent(QResizeEvent* ev) override { +        render_window->resize(ev->size()); +        render_window->OnFramebufferSizeChanged(); +    } -class OpenGLWindow final : public ChildRenderWindow { -public: -    OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) -        : ChildRenderWindow{parent, event_handler}, -          context(new QOpenGLContext(shared_context->parent())) { +    void keyPressEvent(QKeyEvent* event) override { +        InputCommon::GetKeyboard()->PressKey(event->key()); +    } -        // disable vsync for any shared contexts -        auto format = shared_context->format(); -        format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); -        this->setFormat(format); +    void keyReleaseEvent(QKeyEvent* event) override { +        InputCommon::GetKeyboard()->ReleaseKey(event->key()); +    } -        context->setShareContext(shared_context); -        context->setScreen(this->screen()); -        context->setFormat(format); -        context->create(); +    void mousePressEvent(QMouseEvent* event) override { +        if (event->source() == Qt::MouseEventSynthesizedBySystem) +            return; // touch input is handled in TouchBeginEvent -        setSurfaceType(QWindow::OpenGLSurface); +        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 mouseMoveEvent(QMouseEvent* event) override { +        if (event->source() == Qt::MouseEventSynthesizedBySystem) +            return; // touch input is handled in TouchUpdateEvent -        // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, -        // WA_DontShowOnScreen, WA_DeleteOnClose +        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());      } -    ~OpenGLWindow() override { -        context->doneCurrent(); +    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();      } -    void Present() override { -        if (!isExposed()) { -            return; -        } +    std::pair<unsigned, unsigned> GetSize() const { +        return std::make_pair(width(), height()); +    } -        context->makeCurrent(this); -        Core::System::GetInstance().Renderer().TryPresent(100); -        context->swapBuffers(this); -        auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); -        f->glFinish(); -        QWindow::requestUpdate(); +    QPaintEngine* paintEngine() const override { +        return nullptr;      }  private: -    QOpenGLContext* context{}; +    GRenderWindow* render_window;  }; -#ifdef HAS_VULKAN -class VulkanWindow final : public ChildRenderWindow { +class OpenGLRenderWidget : public RenderWidget {  public: -    VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) -        : ChildRenderWindow{parent, event_handler} { -        setSurfaceType(QSurface::SurfaceType::VulkanSurface); -        setVulkanInstance(instance); +    explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { +        windowHandle()->setSurfaceType(QWindow::OpenGLSurface);      } -    ~VulkanWindow() override = default; +    void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { +        context = std::move(context_); +    }      void Present() override { -        // TODO(bunnei): ImplementMe +        if (!isVisible()) { +            return; +        } + +        context->MakeCurrent(); +        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +        if (Core::System::GetInstance().Renderer().TryPresent(100)) { +            context->SwapBuffers(); +            glFinish(); +        }      }  private: -    QWidget* event_handler{}; +    std::unique_ptr<Core::Frontend::GraphicsContext> context{};  }; -#endif -GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) +class VulkanRenderWidget : public RenderWidget { +public: +    explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance) +        : RenderWidget(parent) { +        windowHandle()->setSurfaceType(QWindow::VulkanSurface); +        windowHandle()->setVulkanInstance(instance); +    } +}; + +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<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {      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() { @@ -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<u32, u32> 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<OpenGLSharedContext>(child->windowHandle()); +    main_context = context; +    child->SetContext( +        std::make_unique<OpenGLSharedContext>(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<DummyContext>();      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 79b030304..3739ec7ed 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -7,23 +7,20 @@  #include <atomic>  #include <condition_variable>  #include <mutex> - +#include <thread>  #include <QImage>  #include <QThread>  #include <QWidget>  #include <QWindow> -  #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<Core::Frontend::GraphicsContext> 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<u32, u32> ScaleTouch(const QPointF pos) const; +  public slots:      void OnEmulationStarting(EmuThread* emu_thread);      void OnEmulationStopping(); @@ -176,7 +167,6 @@ signals:      void FirstFrameDisplayed();  private: -    std::pair<u32, u32> 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<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      std::unique_ptr<QVulkanInstance> 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 47615adfe..d7684e241 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<EmuThread>(*render_window); +    emu_thread = std::make_unique<EmuThread>();      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 c0d373477..ee61179a0 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 b80669ff0..e092021d7 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 abcc58165..46d053f04 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 1eb8c0868..3dd1f3f61 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 babf4c3a4..e5db7d819 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()) { | 
