Merge pull request #3552 from jroweboy/single-context
Refactor Context management (Fixes renderdoc on opengl issues)
This commit is contained in:
commit
825a6e2615
32 changed files with 388 additions and 453 deletions
|
@ -131,8 +131,6 @@ add_library(core STATIC
|
||||||
frontend/framebuffer_layout.cpp
|
frontend/framebuffer_layout.cpp
|
||||||
frontend/framebuffer_layout.h
|
frontend/framebuffer_layout.h
|
||||||
frontend/input.h
|
frontend/input.h
|
||||||
frontend/scope_acquire_context.cpp
|
|
||||||
frontend/scope_acquire_context.h
|
|
||||||
gdbstub/gdbstub.cpp
|
gdbstub/gdbstub.cpp
|
||||||
gdbstub/gdbstub.h
|
gdbstub/gdbstub.h
|
||||||
hardware_interrupt_manager.cpp
|
hardware_interrupt_manager.cpp
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "core/file_sys/sdmc_factory.h"
|
#include "core/file_sys/sdmc_factory.h"
|
||||||
#include "core/file_sys/vfs_concat.h"
|
#include "core/file_sys/vfs_concat.h"
|
||||||
#include "core/file_sys/vfs_real.h"
|
#include "core/file_sys/vfs_real.h"
|
||||||
#include "core/frontend/scope_acquire_context.h"
|
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
#include "core/hardware_interrupt_manager.h"
|
#include "core/hardware_interrupt_manager.h"
|
||||||
#include "core/hle/kernel/client_port.h"
|
#include "core/hle/kernel/client_port.h"
|
||||||
|
@ -168,13 +167,12 @@ struct System::Impl {
|
||||||
Service::Init(service_manager, system);
|
Service::Init(service_manager, system);
|
||||||
GDBStub::DeferStart();
|
GDBStub::DeferStart();
|
||||||
|
|
||||||
renderer = VideoCore::CreateRenderer(emu_window, system);
|
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||||
if (!renderer->Init()) {
|
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||||
|
if (!gpu_core) {
|
||||||
return ResultStatus::ErrorVideoCore;
|
return ResultStatus::ErrorVideoCore;
|
||||||
}
|
}
|
||||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
|
||||||
gpu_core = VideoCore::CreateGPU(system);
|
|
||||||
renderer->Rasterizer().SetupDirtyFlags();
|
|
||||||
|
|
||||||
is_powered_on = true;
|
is_powered_on = true;
|
||||||
exit_lock = false;
|
exit_lock = false;
|
||||||
|
@ -186,8 +184,6 @@ struct System::Impl {
|
||||||
|
|
||||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||||
const std::string& filepath) {
|
const std::string& filepath) {
|
||||||
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
|
|
||||||
|
|
||||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||||
if (!app_loader) {
|
if (!app_loader) {
|
||||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||||
|
@ -216,10 +212,6 @@ struct System::Impl {
|
||||||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||||
kernel.MakeCurrentProcess(main_process.get());
|
kernel.MakeCurrentProcess(main_process.get());
|
||||||
|
|
||||||
// Main process has been loaded and been made current.
|
|
||||||
// Begin GPU and CPU execution.
|
|
||||||
gpu_core->Start();
|
|
||||||
|
|
||||||
// Initialize cheat engine
|
// Initialize cheat engine
|
||||||
if (cheat_engine) {
|
if (cheat_engine) {
|
||||||
cheat_engine->Initialize();
|
cheat_engine->Initialize();
|
||||||
|
@ -277,7 +269,6 @@ struct System::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown emulation session
|
// Shutdown emulation session
|
||||||
renderer.reset();
|
|
||||||
GDBStub::Shutdown();
|
GDBStub::Shutdown();
|
||||||
Service::Shutdown();
|
Service::Shutdown();
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
|
@ -353,7 +344,6 @@ struct System::Impl {
|
||||||
Service::FileSystem::FileSystemController fs_controller;
|
Service::FileSystem::FileSystemController fs_controller;
|
||||||
/// AppLoader used to load the current executing application
|
/// AppLoader used to load the current executing application
|
||||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
|
||||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||||
Memory::Memory memory;
|
Memory::Memory memory;
|
||||||
|
@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCore::RendererBase& System::Renderer() {
|
VideoCore::RendererBase& System::Renderer() {
|
||||||
return *impl->renderer;
|
return impl->gpu_core->Renderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoCore::RendererBase& System::Renderer() const {
|
const VideoCore::RendererBase& System::Renderer() const {
|
||||||
return *impl->renderer;
|
return impl->gpu_core->Renderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KernelCore& System::Kernel() {
|
Kernel::KernelCore& System::Kernel() {
|
||||||
|
|
|
@ -13,19 +13,39 @@
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a graphics context that can be used for background computation or drawing. If the
|
* Represents a drawing context that supports graphics operations.
|
||||||
* graphics backend doesn't require the context, then the implementation of these methods can be
|
|
||||||
* stubs
|
|
||||||
*/
|
*/
|
||||||
class GraphicsContext {
|
class GraphicsContext {
|
||||||
public:
|
public:
|
||||||
virtual ~GraphicsContext();
|
virtual ~GraphicsContext();
|
||||||
|
|
||||||
|
/// Inform the driver to swap the front/back buffers and present the current image
|
||||||
|
virtual void SwapBuffers() {}
|
||||||
|
|
||||||
/// Makes the graphics context current for the caller thread
|
/// Makes the graphics context current for the caller thread
|
||||||
virtual void MakeCurrent() = 0;
|
virtual void MakeCurrent() {}
|
||||||
|
|
||||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||||
virtual void DoneCurrent() = 0;
|
virtual void DoneCurrent() {}
|
||||||
|
|
||||||
|
class Scoped {
|
||||||
|
public:
|
||||||
|
explicit Scoped(GraphicsContext& context_) : context(context_) {
|
||||||
|
context.MakeCurrent();
|
||||||
|
}
|
||||||
|
~Scoped() {
|
||||||
|
context.DoneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GraphicsContext& context;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
|
||||||
|
/// ends
|
||||||
|
Scoped Acquire() {
|
||||||
|
return Scoped{*this};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +66,7 @@ public:
|
||||||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||||
* re-read the upper points again and think about it if you don't see this.
|
* re-read the upper points again and think about it if you don't see this.
|
||||||
*/
|
*/
|
||||||
class EmuWindow : public GraphicsContext {
|
class EmuWindow {
|
||||||
public:
|
public:
|
||||||
/// Data structure to store emuwindow configuration
|
/// Data structure to store emuwindow configuration
|
||||||
struct WindowConfig {
|
struct WindowConfig {
|
||||||
|
@ -60,17 +80,9 @@ public:
|
||||||
virtual void PollEvents() = 0;
|
virtual void PollEvents() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
|
* Returns a GraphicsContext that the frontend provides to be used for rendering.
|
||||||
* context can be used from other threads for background graphics computation. If the frontend
|
|
||||||
* is using a graphics backend that doesn't need anything specific to run on a different thread,
|
|
||||||
* then it can use a stubbed implemenation for GraphicsContext.
|
|
||||||
*
|
|
||||||
* If the return value is null, then the core should assume that the frontend cannot provide a
|
|
||||||
* Shared Context
|
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
|
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns if window is shown (not minimized)
|
/// Returns if window is shown (not minimized)
|
||||||
virtual bool IsShown() const = 0;
|
virtual bool IsShown() const = 0;
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "core/frontend/emu_window.h"
|
|
||||||
#include "core/frontend/scope_acquire_context.h"
|
|
||||||
|
|
||||||
namespace Core::Frontend {
|
|
||||||
|
|
||||||
ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
|
|
||||||
: context{context} {
|
|
||||||
context.MakeCurrent();
|
|
||||||
}
|
|
||||||
ScopeAcquireContext::~ScopeAcquireContext() {
|
|
||||||
context.DoneCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Core::Frontend
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Core::Frontend {
|
|
||||||
|
|
||||||
class GraphicsContext;
|
|
||||||
|
|
||||||
/// Helper class to acquire/release window context within a given scope
|
|
||||||
class ScopeAcquireContext : NonCopyable {
|
|
||||||
public:
|
|
||||||
explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
|
|
||||||
~ScopeAcquireContext();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Core::Frontend::GraphicsContext& context;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Core::Frontend
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
#include "core/core_timing_util.h"
|
||||||
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "video_core/engines/fermi_2d.h"
|
#include "video_core/engines/fermi_2d.h"
|
||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
|
@ -16,14 +17,15 @@
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
|
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
|
||||||
|
|
||||||
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
|
GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async)
|
||||||
: system{system}, renderer{renderer}, is_async{is_async} {
|
: system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
|
||||||
auto& rasterizer{renderer.Rasterizer()};
|
auto& rasterizer{renderer->Rasterizer()};
|
||||||
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
|
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
|
||||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
||||||
|
@ -137,7 +139,7 @@ u64 GPU::GetTicks() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::FlushCommands() {
|
void GPU::FlushCommands() {
|
||||||
renderer.Rasterizer().FlushCommands();
|
renderer->Rasterizer().FlushCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
|
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
|
||||||
|
|
|
@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
namespace Frontend {
|
||||||
|
class EmuWindow;
|
||||||
}
|
}
|
||||||
|
class System;
|
||||||
|
} // namespace Core
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
|
@ -129,7 +132,8 @@ class MemoryManager;
|
||||||
|
|
||||||
class GPU {
|
class GPU {
|
||||||
public:
|
public:
|
||||||
explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async);
|
explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||||
|
bool is_async);
|
||||||
|
|
||||||
virtual ~GPU();
|
virtual ~GPU();
|
||||||
|
|
||||||
|
@ -174,6 +178,14 @@ public:
|
||||||
/// Returns a reference to the GPU DMA pusher.
|
/// Returns a reference to the GPU DMA pusher.
|
||||||
Tegra::DmaPusher& DmaPusher();
|
Tegra::DmaPusher& DmaPusher();
|
||||||
|
|
||||||
|
VideoCore::RendererBase& Renderer() {
|
||||||
|
return *renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VideoCore::RendererBase& Renderer() const {
|
||||||
|
return *renderer;
|
||||||
|
}
|
||||||
|
|
||||||
// Waits for the GPU to finish working
|
// Waits for the GPU to finish working
|
||||||
virtual void WaitIdle() const = 0;
|
virtual void WaitIdle() const = 0;
|
||||||
|
|
||||||
|
@ -287,7 +299,7 @@ private:
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
|
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
VideoCore::RendererBase& renderer;
|
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||||
|
|
|
@ -10,13 +10,16 @@
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
|
GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_,
|
||||||
: GPU(system, renderer, true), gpu_thread{system} {}
|
std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
|
||||||
|
: GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)),
|
||||||
|
cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {}
|
||||||
|
|
||||||
GPUAsynch::~GPUAsynch() = default;
|
GPUAsynch::~GPUAsynch() = default;
|
||||||
|
|
||||||
void GPUAsynch::Start() {
|
void GPUAsynch::Start() {
|
||||||
gpu_thread.StartThread(renderer, *dma_pusher);
|
cpu_context->MakeCurrent();
|
||||||
|
gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/gpu_thread.h"
|
#include "video_core/gpu_thread.h"
|
||||||
|
|
||||||
|
namespace Core::Frontend {
|
||||||
|
class GraphicsContext;
|
||||||
|
}
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
@ -16,7 +20,8 @@ namespace VideoCommon {
|
||||||
/// Implementation of GPU interface that runs the GPU asynchronously
|
/// Implementation of GPU interface that runs the GPU asynchronously
|
||||||
class GPUAsynch final : public Tegra::GPU {
|
class GPUAsynch final : public Tegra::GPU {
|
||||||
public:
|
public:
|
||||||
explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
|
explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
|
||||||
~GPUAsynch() override;
|
~GPUAsynch() override;
|
||||||
|
|
||||||
void Start() override;
|
void Start() override;
|
||||||
|
@ -32,6 +37,8 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GPUThread::ThreadManager gpu_thread;
|
GPUThread::ThreadManager gpu_thread;
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
||||||
|
|
|
@ -7,12 +7,15 @@
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
|
GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||||
: GPU(system, renderer, false) {}
|
std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
|
||||||
|
: GPU(system, std::move(renderer), false), context{std::move(context)} {}
|
||||||
|
|
||||||
GPUSynch::~GPUSynch() = default;
|
GPUSynch::~GPUSynch() = default;
|
||||||
|
|
||||||
void GPUSynch::Start() {}
|
void GPUSynch::Start() {
|
||||||
|
context->MakeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||||
dma_pusher->Push(std::move(entries));
|
dma_pusher->Push(std::move(entries));
|
||||||
|
@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
renderer.SwapBuffers(framebuffer);
|
renderer->SwapBuffers(framebuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
|
void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
|
||||||
renderer.Rasterizer().FlushRegion(addr, size);
|
renderer->Rasterizer().FlushRegion(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
|
void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
|
||||||
renderer.Rasterizer().InvalidateRegion(addr, size);
|
renderer->Rasterizer().InvalidateRegion(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
||||||
renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
|
renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
|
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
|
|
||||||
|
namespace Core::Frontend {
|
||||||
|
class GraphicsContext;
|
||||||
|
}
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
@ -15,7 +19,8 @@ namespace VideoCommon {
|
||||||
/// Implementation of GPU interface that runs the GPU synchronously
|
/// Implementation of GPU interface that runs the GPU synchronously
|
||||||
class GPUSynch final : public Tegra::GPU {
|
class GPUSynch final : public Tegra::GPU {
|
||||||
public:
|
public:
|
||||||
explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
|
explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
|
||||||
~GPUSynch() override;
|
~GPUSynch() override;
|
||||||
|
|
||||||
void Start() override;
|
void Start() override;
|
||||||
|
@ -29,6 +34,9 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
|
void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
|
||||||
[[maybe_unused]] u32 value) const override {}
|
[[maybe_unused]] u32 value) const override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> context;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/scope_acquire_context.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "video_core/dma_pusher.h"
|
#include "video_core/dma_pusher.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/gpu_thread.h"
|
#include "video_core/gpu_thread.h"
|
||||||
|
@ -14,8 +14,8 @@
|
||||||
namespace VideoCommon::GPUThread {
|
namespace VideoCommon::GPUThread {
|
||||||
|
|
||||||
/// Runs the GPU thread
|
/// Runs the GPU thread
|
||||||
static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
|
static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||||
SynchState& state) {
|
Tegra::DmaPusher& dma_pusher, SynchState& state) {
|
||||||
MicroProfileOnThreadCreate("GpuThread");
|
MicroProfileOnThreadCreate("GpuThread");
|
||||||
|
|
||||||
// Wait for first GPU command before acquiring the window context
|
// Wait for first GPU command before acquiring the window context
|
||||||
|
@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
|
auto current_context = context.Acquire();
|
||||||
|
|
||||||
CommandDataContainer next;
|
CommandDataContainer next;
|
||||||
while (state.is_running) {
|
while (state.is_running) {
|
||||||
|
@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() {
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
|
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
||||||
thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
|
Core::Frontend::GraphicsContext& context,
|
||||||
|
Tegra::DmaPusher& dma_pusher) {
|
||||||
|
thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher),
|
||||||
|
std::ref(state)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/threadsafe_queue.h"
|
#include "common/threadsafe_queue.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
|
|
||||||
|
@ -20,6 +19,9 @@ class DmaPusher;
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
namespace Frontend {
|
||||||
|
class GraphicsContext;
|
||||||
|
}
|
||||||
class System;
|
class System;
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
||||||
|
@ -99,7 +101,8 @@ public:
|
||||||
~ThreadManager();
|
~ThreadManager();
|
||||||
|
|
||||||
/// Creates and starts the GPU thread.
|
/// Creates and starts the GPU thread.
|
||||||
void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
|
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||||
|
Tegra::DmaPusher& dma_pusher);
|
||||||
|
|
||||||
/// Push GPU command entries to be processed
|
/// Push GPU command entries to be processed
|
||||||
void SubmitList(Tegra::CommandList&& entries);
|
void SubmitList(Tegra::CommandList&& entries);
|
||||||
|
|
|
@ -46,7 +46,8 @@ public:
|
||||||
|
|
||||||
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
|
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
|
||||||
/// specific implementation)
|
/// specific implementation)
|
||||||
virtual void TryPresent(int timeout_ms) = 0;
|
/// Returns true if a frame was drawn
|
||||||
|
virtual bool TryPresent(int timeout_ms) = 0;
|
||||||
|
|
||||||
// Getter/setter functions:
|
// Getter/setter functions:
|
||||||
// ------------------------
|
// ------------------------
|
||||||
|
|
|
@ -327,8 +327,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
|
|
||||||
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
||||||
std::size_t end) {
|
std::size_t end) {
|
||||||
context->MakeCurrent();
|
const auto scope = context->Acquire();
|
||||||
SCOPE_EXIT({ return context->DoneCurrent(); });
|
|
||||||
|
|
||||||
for (std::size_t i = begin; i < end; ++i) {
|
for (std::size_t i = begin; i < end; ++i) {
|
||||||
if (stop_loading) {
|
if (stop_loading) {
|
||||||
|
|
|
@ -30,8 +30,6 @@ namespace OpenGL {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
|
|
||||||
// to wait on available presentation frames.
|
|
||||||
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
|
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
|
@ -214,7 +212,7 @@ public:
|
||||||
std::deque<Frame*> present_queue;
|
std::deque<Frame*> present_queue;
|
||||||
Frame* previous_frame{};
|
Frame* previous_frame{};
|
||||||
|
|
||||||
FrameMailbox() : has_debug_tool{HasDebugTool()} {
|
FrameMailbox() {
|
||||||
for (auto& frame : swap_chain) {
|
for (auto& frame : swap_chain) {
|
||||||
free_queue.push(&frame);
|
free_queue.push(&frame);
|
||||||
}
|
}
|
||||||
|
@ -285,13 +283,9 @@ public:
|
||||||
std::unique_lock lock{swap_chain_lock};
|
std::unique_lock lock{swap_chain_lock};
|
||||||
present_queue.push_front(frame);
|
present_queue.push_front(frame);
|
||||||
present_cv.notify_one();
|
present_cv.notify_one();
|
||||||
|
|
||||||
DebugNotifyNextFrame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame* TryGetPresentFrame(int timeout_ms) {
|
Frame* TryGetPresentFrame(int timeout_ms) {
|
||||||
DebugWaitForNextFrame();
|
|
||||||
|
|
||||||
std::unique_lock lock{swap_chain_lock};
|
std::unique_lock lock{swap_chain_lock};
|
||||||
// wait for new entries in the present_queue
|
// wait for new entries in the present_queue
|
||||||
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
||||||
|
@ -317,38 +311,12 @@ public:
|
||||||
previous_frame = frame;
|
previous_frame = frame;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex debug_synch_mutex;
|
|
||||||
std::condition_variable debug_synch_condition;
|
|
||||||
std::atomic_int frame_for_debug{};
|
|
||||||
const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step
|
|
||||||
|
|
||||||
/// Signal that a new frame is available (called from GPU thread)
|
|
||||||
void DebugNotifyNextFrame() {
|
|
||||||
if (!has_debug_tool) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
frame_for_debug++;
|
|
||||||
std::lock_guard lock{debug_synch_mutex};
|
|
||||||
debug_synch_condition.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wait for a new frame to be available (called from presentation thread)
|
|
||||||
void DebugWaitForNextFrame() {
|
|
||||||
if (!has_debug_tool) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const int last_frame = frame_for_debug;
|
|
||||||
std::unique_lock lock{debug_synch_mutex};
|
|
||||||
debug_synch_condition.wait(lock,
|
|
||||||
[this, last_frame] { return frame_for_debug > last_frame; });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
|
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
|
||||||
|
Core::Frontend::GraphicsContext& context)
|
||||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
|
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
|
||||||
frame_mailbox{std::make_unique<FrameMailbox>()} {}
|
frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {}
|
||||||
|
|
||||||
RendererOpenGL::~RendererOpenGL() = default;
|
RendererOpenGL::~RendererOpenGL() = default;
|
||||||
|
|
||||||
|
@ -356,8 +324,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12
|
||||||
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
||||||
|
|
||||||
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
render_window.PollEvents();
|
|
||||||
|
|
||||||
if (!framebuffer) {
|
if (!framebuffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
m_current_frame++;
|
m_current_frame++;
|
||||||
rasterizer->TickFrame();
|
rasterizer->TickFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_window.PollEvents();
|
||||||
|
if (has_debug_tool) {
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
Present(0);
|
||||||
|
context.SwapBuffers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
|
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
|
@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::InitOpenGLObjects() {
|
void RendererOpenGL::InitOpenGLObjects() {
|
||||||
|
frame_mailbox = std::make_unique<FrameMailbox>();
|
||||||
|
|
||||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||||
0.0f);
|
0.0f);
|
||||||
|
|
||||||
|
@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::TryPresent(int timeout_ms) {
|
bool RendererOpenGL::TryPresent(int timeout_ms) {
|
||||||
|
if (has_debug_tool) {
|
||||||
|
LOG_DEBUG(Render_OpenGL,
|
||||||
|
"Skipping presentation because we are presenting on the main context");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Present(timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RendererOpenGL::Present(int timeout_ms) {
|
||||||
const auto& layout = render_window.GetFramebufferLayout();
|
const auto& layout = render_window.GetFramebufferLayout();
|
||||||
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
|
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
|
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
|
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
|
||||||
|
@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
|
||||||
glFlush();
|
glFlush();
|
||||||
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::RenderScreenshot() {
|
void RendererOpenGL::RenderScreenshot() {
|
||||||
|
|
|
@ -55,13 +55,14 @@ class FrameMailbox;
|
||||||
|
|
||||||
class RendererOpenGL final : public VideoCore::RendererBase {
|
class RendererOpenGL final : public VideoCore::RendererBase {
|
||||||
public:
|
public:
|
||||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
|
||||||
|
Core::Frontend::GraphicsContext& context);
|
||||||
~RendererOpenGL() override;
|
~RendererOpenGL() override;
|
||||||
|
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
void ShutDown() override;
|
void ShutDown() override;
|
||||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||||
void TryPresent(int timeout_ms) override;
|
bool TryPresent(int timeout_ms) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Initializes the OpenGL state and creates persistent objects.
|
/// Initializes the OpenGL state and creates persistent objects.
|
||||||
|
@ -89,8 +90,11 @@ private:
|
||||||
|
|
||||||
void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
|
void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
|
||||||
|
|
||||||
|
bool Present(int timeout_ms);
|
||||||
|
|
||||||
Core::Frontend::EmuWindow& emu_window;
|
Core::Frontend::EmuWindow& emu_window;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
Core::Frontend::GraphicsContext& context;
|
||||||
|
|
||||||
StateTracker state_tracker{system};
|
StateTracker state_tracker{system};
|
||||||
|
|
||||||
|
@ -115,6 +119,8 @@ private:
|
||||||
|
|
||||||
/// Frame presentation mailbox
|
/// Frame presentation mailbox
|
||||||
std::unique_ptr<FrameMailbox> frame_mailbox;
|
std::unique_ptr<FrameMailbox> frame_mailbox;
|
||||||
|
|
||||||
|
bool has_debug_tool = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
|
|
@ -141,8 +141,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
render_window.PollEvents();
|
render_window.PollEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererVulkan::TryPresent(int /*timeout_ms*/) {
|
bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
|
||||||
// TODO (bunnei): ImplementMe
|
// TODO (bunnei): ImplementMe
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RendererVulkan::Init() {
|
bool RendererVulkan::Init() {
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
void ShutDown() override;
|
void ShutDown() override;
|
||||||
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
|
||||||
void TryPresent(int timeout_ms) override;
|
bool TryPresent(int timeout_ms) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
|
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
#endif
|
#endif
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace {
|
||||||
|
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
Core::System& system,
|
||||||
Core::System& system) {
|
Core::Frontend::GraphicsContext& context) {
|
||||||
switch (Settings::values.renderer_backend) {
|
switch (Settings::values.renderer_backend) {
|
||||||
case Settings::RendererBackend::OpenGL:
|
case Settings::RendererBackend::OpenGL:
|
||||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
|
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
case Settings::RendererBackend::Vulkan:
|
case Settings::RendererBackend::Vulkan:
|
||||||
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
|
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
|
||||||
|
@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
|
namespace VideoCore {
|
||||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
|
||||||
return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
|
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
||||||
|
auto context = emu_window.CreateSharedContext();
|
||||||
|
const auto scope = context->Acquire();
|
||||||
|
auto renderer = CreateRenderer(emu_window, system, *context);
|
||||||
|
if (!renderer->Init()) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
|
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||||
|
return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
|
||||||
|
std::move(context));
|
||||||
|
}
|
||||||
|
return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||||
|
|
|
@ -22,17 +22,8 @@ namespace VideoCore {
|
||||||
|
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a renderer instance.
|
|
||||||
*
|
|
||||||
* @note The returned renderer instance is simply allocated. Its Init()
|
|
||||||
* function still needs to be called to fully complete its setup.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
|
||||||
Core::System& system);
|
|
||||||
|
|
||||||
/// Creates an emulated GPU instance using the given system context.
|
/// Creates an emulated GPU instance using the given system context.
|
||||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
|
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||||
|
|
||||||
u16 GetResolutionScaleFactor(const RendererBase& renderer);
|
u16 GetResolutionScaleFactor(const RendererBase& renderer);
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,6 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QOffscreenSurface>
|
#include <QOffscreenSurface>
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <QOpenGLFunctions>
|
|
||||||
#include <QOpenGLFunctions_4_3_Core>
|
|
||||||
#include <QOpenGLWindow>
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
@ -29,7 +26,6 @@
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
#include "core/frontend/scope_acquire_context.h"
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "input_common/keyboard.h"
|
#include "input_common/keyboard.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
|
@ -39,26 +35,16 @@
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
#include "yuzu/main.h"
|
#include "yuzu/main.h"
|
||||||
|
|
||||||
EmuThread::EmuThread(GRenderWindow& window)
|
EmuThread::EmuThread() = default;
|
||||||
: shared_context{window.CreateSharedContext()},
|
|
||||||
context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
|
|
||||||
: window} {}
|
|
||||||
|
|
||||||
EmuThread::~EmuThread() = default;
|
EmuThread::~EmuThread() = default;
|
||||||
|
|
||||||
static GMainWindow* GetMainWindow() {
|
|
||||||
for (QWidget* w : qApp->topLevelWidgets()) {
|
|
||||||
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
|
|
||||||
return main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuThread::run() {
|
void EmuThread::run() {
|
||||||
MicroProfileOnThreadCreate("EmuThread");
|
MicroProfileOnThreadCreate("EmuThread");
|
||||||
|
|
||||||
Core::Frontend::ScopeAcquireContext acquire_context{context};
|
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
|
||||||
|
// execution.
|
||||||
|
Core::System::GetInstance().GPU().Start();
|
||||||
|
|
||||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
|
|
||||||
|
@ -111,162 +97,156 @@ void EmuThread::run() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
class GGLContext : public Core::Frontend::GraphicsContext {
|
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
|
||||||
public:
|
public:
|
||||||
explicit GGLContext(QOpenGLContext* shared_context)
|
/// Create the original context that should be shared from
|
||||||
: context(new QOpenGLContext(shared_context->parent())),
|
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
|
||||||
surface(new QOffscreenSurface(nullptr)) {
|
QSurfaceFormat format;
|
||||||
|
format.setVersion(4, 3);
|
||||||
// disable vsync for any shared contexts
|
format.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||||
auto format = shared_context->format();
|
format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
||||||
|
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||||
|
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||||
format.setSwapInterval(0);
|
format.setSwapInterval(0);
|
||||||
|
|
||||||
context->setShareContext(shared_context);
|
context = std::make_unique<QOpenGLContext>();
|
||||||
context->setFormat(format);
|
context->setFormat(format);
|
||||||
context->create();
|
if (!context->create()) {
|
||||||
surface->setParent(shared_context->parent());
|
LOG_ERROR(Frontend, "Unable to create main openGL context");
|
||||||
surface->setFormat(format);
|
}
|
||||||
surface->create();
|
}
|
||||||
|
|
||||||
|
/// Create the shared contexts for rendering and presentation
|
||||||
|
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
|
||||||
|
|
||||||
|
// disable vsync for any shared contexts
|
||||||
|
auto format = share_context->format();
|
||||||
|
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
|
||||||
|
|
||||||
|
context = std::make_unique<QOpenGLContext>();
|
||||||
|
context->setShareContext(share_context);
|
||||||
|
context->setFormat(format);
|
||||||
|
if (!context->create()) {
|
||||||
|
LOG_ERROR(Frontend, "Unable to create shared openGL context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!main_surface) {
|
||||||
|
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
|
||||||
|
offscreen_surface->setFormat(format);
|
||||||
|
offscreen_surface->create();
|
||||||
|
surface = offscreen_surface.get();
|
||||||
|
} else {
|
||||||
|
surface = main_surface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~OpenGLSharedContext() {
|
||||||
|
DoneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapBuffers() override {
|
||||||
|
context->swapBuffers(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MakeCurrent() override {
|
void MakeCurrent() override {
|
||||||
context->makeCurrent(surface);
|
if (is_current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
is_current = context->makeCurrent(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoneCurrent() override {
|
void DoneCurrent() override {
|
||||||
|
if (!is_current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
context->doneCurrent();
|
context->doneCurrent();
|
||||||
|
is_current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QOpenGLContext* GetShareContext() {
|
||||||
|
return context.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QOpenGLContext* GetShareContext() const {
|
||||||
|
return context.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QOpenGLContext* context;
|
// Avoid using Qt parent system here since we might move the QObjects to new threads
|
||||||
QOffscreenSurface* surface;
|
// As a note, this means we should avoid using slots/signals with the objects too
|
||||||
|
std::unique_ptr<QOpenGLContext> context;
|
||||||
|
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
|
||||||
|
QSurface* surface;
|
||||||
|
bool is_current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChildRenderWindow : public QWindow {
|
class DummyContext : public Core::Frontend::GraphicsContext {};
|
||||||
|
|
||||||
|
class RenderWidget : public QWidget {
|
||||||
public:
|
public:
|
||||||
ChildRenderWindow(QWindow* parent, QWidget* event_handler)
|
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
|
||||||
: QWindow{parent}, event_handler{event_handler} {}
|
setAttribute(Qt::WA_NativeWindow);
|
||||||
|
setAttribute(Qt::WA_PaintOnScreen);
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~ChildRenderWindow() = default;
|
virtual ~RenderWidget() = default;
|
||||||
|
|
||||||
virtual void Present() = 0;
|
/// Called on the UI thread when this Widget is ready to draw
|
||||||
|
/// Dervied classes can override this to draw the latest frame.
|
||||||
|
virtual void Present() {}
|
||||||
|
|
||||||
protected:
|
void paintEvent(QPaintEvent* event) override {
|
||||||
bool event(QEvent* event) override {
|
|
||||||
switch (event->type()) {
|
|
||||||
case QEvent::UpdateRequest:
|
|
||||||
Present();
|
Present();
|
||||||
return true;
|
update();
|
||||||
case QEvent::MouseButtonPress:
|
|
||||||
case QEvent::MouseButtonRelease:
|
|
||||||
case QEvent::MouseButtonDblClick:
|
|
||||||
case QEvent::MouseMove:
|
|
||||||
case QEvent::KeyPress:
|
|
||||||
case QEvent::KeyRelease:
|
|
||||||
case QEvent::FocusIn:
|
|
||||||
case QEvent::FocusOut:
|
|
||||||
case QEvent::FocusAboutToChange:
|
|
||||||
case QEvent::Enter:
|
|
||||||
case QEvent::Leave:
|
|
||||||
case QEvent::Wheel:
|
|
||||||
case QEvent::TabletMove:
|
|
||||||
case QEvent::TabletPress:
|
|
||||||
case QEvent::TabletRelease:
|
|
||||||
case QEvent::TabletEnterProximity:
|
|
||||||
case QEvent::TabletLeaveProximity:
|
|
||||||
case QEvent::TouchBegin:
|
|
||||||
case QEvent::TouchUpdate:
|
|
||||||
case QEvent::TouchEnd:
|
|
||||||
case QEvent::InputMethodQuery:
|
|
||||||
case QEvent::TouchCancel:
|
|
||||||
return QCoreApplication::sendEvent(event_handler, event);
|
|
||||||
case QEvent::Drop:
|
|
||||||
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
|
|
||||||
return true;
|
|
||||||
case QEvent::DragResponse:
|
|
||||||
case QEvent::DragEnter:
|
|
||||||
case QEvent::DragLeave:
|
|
||||||
case QEvent::DragMove:
|
|
||||||
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return QWindow::event(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void exposeEvent(QExposeEvent* event) override {
|
QPaintEngine* paintEngine() const override {
|
||||||
QWindow::requestUpdate();
|
return nullptr;
|
||||||
QWindow::exposeEvent(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* event_handler{};
|
GRenderWindow* render_window;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpenGLWindow final : public ChildRenderWindow {
|
class OpenGLRenderWidget : public RenderWidget {
|
||||||
public:
|
public:
|
||||||
OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
|
explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
|
||||||
: ChildRenderWindow{parent, event_handler},
|
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
|
||||||
context(new QOpenGLContext(shared_context->parent())) {
|
|
||||||
|
|
||||||
// disable vsync for any shared contexts
|
|
||||||
auto format = shared_context->format();
|
|
||||||
format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
|
|
||||||
this->setFormat(format);
|
|
||||||
|
|
||||||
context->setShareContext(shared_context);
|
|
||||||
context->setScreen(this->screen());
|
|
||||||
context->setFormat(format);
|
|
||||||
context->create();
|
|
||||||
|
|
||||||
setSurfaceType(QWindow::OpenGLSurface);
|
|
||||||
|
|
||||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
|
||||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~OpenGLWindow() override {
|
void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) {
|
||||||
context->doneCurrent();
|
context = std::move(context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Present() override {
|
void Present() override {
|
||||||
if (!isExposed()) {
|
if (!isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->makeCurrent(this);
|
context->MakeCurrent();
|
||||||
Core::System::GetInstance().Renderer().TryPresent(100);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
context->swapBuffers(this);
|
if (Core::System::GetInstance().Renderer().TryPresent(100)) {
|
||||||
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
|
context->SwapBuffers();
|
||||||
f->glFinish();
|
glFinish();
|
||||||
QWindow::requestUpdate();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QOpenGLContext* context{};
|
std::unique_ptr<Core::Frontend::GraphicsContext> context{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
class VulkanWindow final : public ChildRenderWindow {
|
class VulkanRenderWidget : public RenderWidget {
|
||||||
public:
|
public:
|
||||||
VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance)
|
explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
|
||||||
: ChildRenderWindow{parent, event_handler} {
|
: RenderWidget(parent) {
|
||||||
setSurfaceType(QSurface::SurfaceType::VulkanSurface);
|
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||||
setVulkanInstance(instance);
|
windowHandle()->setVulkanInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
~VulkanWindow() override = default;
|
|
||||||
|
|
||||||
void Present() override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QWidget* event_handler{};
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
|
||||||
: QWidget(parent_), emu_thread(emu_thread) {
|
: QWidget(parent_), emu_thread(emu_thread) {
|
||||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||||
.arg(QString::fromUtf8(Common::g_build_name),
|
.arg(QString::fromUtf8(Common::g_build_name),
|
||||||
|
@ -278,26 +258,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
InputCommon::Init();
|
InputCommon::Init();
|
||||||
|
|
||||||
GMainWindow* parent = GetMainWindow();
|
connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
|
||||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GRenderWindow::~GRenderWindow() {
|
GRenderWindow::~GRenderWindow() {
|
||||||
InputCommon::Shutdown();
|
InputCommon::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::MakeCurrent() {
|
|
||||||
if (core_context) {
|
|
||||||
core_context->MakeCurrent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GRenderWindow::DoneCurrent() {
|
|
||||||
if (core_context) {
|
|
||||||
core_context->DoneCurrent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GRenderWindow::PollEvents() {
|
void GRenderWindow::PollEvents() {
|
||||||
if (!first_frame) {
|
if (!first_frame) {
|
||||||
first_frame = true;
|
first_frame = true;
|
||||||
|
@ -309,21 +276,6 @@ bool GRenderWindow::IsShown() const {
|
||||||
return !isMinimized();
|
return !isMinimized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const {
|
|
||||||
#ifdef HAS_VULKAN
|
|
||||||
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
|
|
||||||
const VkInstance instance_copy = vk_instance->vkInstance();
|
|
||||||
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
|
|
||||||
|
|
||||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
|
||||||
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
|
|
||||||
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
|
|
||||||
#else
|
|
||||||
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
|
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
|
||||||
//
|
//
|
||||||
// Older versions get the window size (density independent pixels),
|
// Older versions get the window size (density independent pixels),
|
||||||
|
@ -367,7 +319,7 @@ qreal GRenderWindow::windowPixelRatio() const {
|
||||||
return devicePixelRatio();
|
return devicePixelRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
|
||||||
const qreal pixel_ratio = windowPixelRatio();
|
const qreal pixel_ratio = windowPixelRatio();
|
||||||
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||||
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||||
|
@ -387,8 +339,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
// touch input is handled in TouchBeginEvent
|
||||||
return; // touch input is handled in TouchBeginEvent
|
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto pos = event->pos();
|
auto pos = event->pos();
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
@ -400,8 +354,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
// touch input is handled in TouchUpdateEvent
|
||||||
return; // touch input is handled in TouchUpdateEvent
|
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto pos = event->pos();
|
auto pos = event->pos();
|
||||||
const auto [x, y] = ScaleTouch(pos);
|
const auto [x, y] = ScaleTouch(pos);
|
||||||
|
@ -410,14 +366,17 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
// touch input is handled in TouchEndEvent
|
||||||
return; // touch input is handled in TouchEndEvent
|
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event->button() == Qt::LeftButton)
|
if (event->button() == Qt::LeftButton) {
|
||||||
this->TouchReleased();
|
this->TouchReleased();
|
||||||
else if (event->button() == Qt::RightButton)
|
} else if (event->button() == Qt::RightButton) {
|
||||||
InputCommon::GetMotionEmu()->EndTilt();
|
InputCommon::GetMotionEmu()->EndTilt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||||
// TouchBegin always has exactly one touch point, so take the .first()
|
// TouchBegin always has exactly one touch point, so take the .first()
|
||||||
|
@ -474,9 +433,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||||
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
||||||
return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
|
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
|
||||||
|
// Bind the shared contexts to the main surface in case the backend wants to take over
|
||||||
|
// presentation
|
||||||
|
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
|
||||||
|
child_widget->windowHandle());
|
||||||
}
|
}
|
||||||
return {};
|
return std::make_unique<DummyContext>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GRenderWindow::InitRenderTarget() {
|
bool GRenderWindow::InitRenderTarget() {
|
||||||
|
@ -497,14 +460,11 @@ bool GRenderWindow::InitRenderTarget() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||||
|
layout()->addWidget(child_widget);
|
||||||
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
||||||
setMinimumSize(1, 1);
|
setMinimumSize(1, 1);
|
||||||
|
|
||||||
// Show causes the window to actually be created and the gl context as well, but we don't want
|
|
||||||
// the widget to be shown yet, so immediately hide it.
|
|
||||||
show();
|
|
||||||
hide();
|
|
||||||
|
|
||||||
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||||
|
|
||||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||||
|
@ -523,9 +483,10 @@ bool GRenderWindow::InitRenderTarget() {
|
||||||
void GRenderWindow::ReleaseRenderTarget() {
|
void GRenderWindow::ReleaseRenderTarget() {
|
||||||
if (child_widget) {
|
if (child_widget) {
|
||||||
layout()->removeWidget(child_widget);
|
layout()->removeWidget(child_widget);
|
||||||
delete child_widget;
|
child_widget->deleteLater();
|
||||||
child_widget = nullptr;
|
child_widget = nullptr;
|
||||||
}
|
}
|
||||||
|
main_context.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||||
|
@ -557,24 +518,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||||
bool GRenderWindow::InitializeOpenGL() {
|
bool GRenderWindow::InitializeOpenGL() {
|
||||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||||
QSurfaceFormat fmt;
|
auto child = new OpenGLRenderWidget(this);
|
||||||
fmt.setVersion(4, 3);
|
child_widget = child;
|
||||||
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
|
child_widget->windowHandle()->create();
|
||||||
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
|
||||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
main_context = context;
|
||||||
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
child->SetContext(
|
||||||
fmt.setSwapInterval(0);
|
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
|
||||||
QSurfaceFormat::setDefaultFormat(fmt);
|
|
||||||
|
|
||||||
GMainWindow* parent = GetMainWindow();
|
|
||||||
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
|
|
||||||
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
|
|
||||||
child_window->create();
|
|
||||||
child_widget = createWindowContainer(child_window, this);
|
|
||||||
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
|
||||||
layout()->addWidget(child_widget);
|
|
||||||
|
|
||||||
core_context = CreateSharedContext();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -604,13 +554,10 @@ bool GRenderWindow::InitializeVulkan() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GMainWindow* parent = GetMainWindow();
|
auto child = new VulkanRenderWidget(this, vk_instance.get());
|
||||||
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
|
child_widget = child;
|
||||||
child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
|
child_widget->windowHandle()->create();
|
||||||
child_window->create();
|
main_context = std::make_unique<DummyContext>();
|
||||||
child_widget = createWindowContainer(child_window, this);
|
|
||||||
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
|
||||||
layout()->addWidget(child_widget);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
@ -620,8 +567,24 @@ bool GRenderWindow::InitializeVulkan() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||||
|
void* surface) const {
|
||||||
|
#ifdef HAS_VULKAN
|
||||||
|
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
|
||||||
|
const VkInstance instance_copy = vk_instance->vkInstance();
|
||||||
|
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle());
|
||||||
|
|
||||||
|
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
||||||
|
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
|
||||||
|
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
|
||||||
|
#else
|
||||||
|
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool GRenderWindow::LoadOpenGL() {
|
bool GRenderWindow::LoadOpenGL() {
|
||||||
Core::Frontend::ScopeAcquireContext acquire_context{*this};
|
auto context = CreateSharedContext();
|
||||||
|
auto scope = context->Acquire();
|
||||||
if (!gladLoadGL()) {
|
if (!gladLoadGL()) {
|
||||||
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
|
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
|
||||||
tr("Your GPU may not support OpenGL 4.3, or you do not have the "
|
tr("Your GPU may not support OpenGL 4.3, or you do not have the "
|
||||||
|
|
|
@ -18,12 +18,10 @@
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
|
||||||
class GRenderWindow;
|
class GRenderWindow;
|
||||||
|
class GMainWindow;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
class QScreen;
|
|
||||||
class QTouchEvent;
|
class QTouchEvent;
|
||||||
class QStringList;
|
class QStringList;
|
||||||
class QSurface;
|
|
||||||
class QOpenGLContext;
|
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
class QVulkanInstance;
|
class QVulkanInstance;
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,7 +34,7 @@ class EmuThread final : public QThread {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EmuThread(GRenderWindow& window);
|
explicit EmuThread();
|
||||||
~EmuThread() override;
|
~EmuThread() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,12 +88,6 @@ private:
|
||||||
std::mutex running_mutex;
|
std::mutex running_mutex;
|
||||||
std::condition_variable running_cv;
|
std::condition_variable running_cv;
|
||||||
|
|
||||||
/// Only used in asynchronous GPU mode
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> shared_context;
|
|
||||||
|
|
||||||
/// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode
|
|
||||||
Core::Frontend::GraphicsContext& context;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* Emitted when the CPU has halted execution
|
* Emitted when the CPU has halted execution
|
||||||
|
@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
|
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
|
||||||
~GRenderWindow() override;
|
~GRenderWindow() override;
|
||||||
|
|
||||||
// EmuWindow implementation.
|
// EmuWindow implementation.
|
||||||
void MakeCurrent() override;
|
|
||||||
void DoneCurrent() override;
|
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
bool IsShown() const override;
|
bool IsShown() const override;
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||||
|
@ -165,6 +155,8 @@ public:
|
||||||
|
|
||||||
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
||||||
|
|
||||||
|
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnEmulationStarting(EmuThread* emu_thread);
|
void OnEmulationStarting(EmuThread* emu_thread);
|
||||||
void OnEmulationStopping();
|
void OnEmulationStopping();
|
||||||
|
@ -176,7 +168,6 @@ signals:
|
||||||
void FirstFrameDisplayed();
|
void FirstFrameDisplayed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
|
|
||||||
void TouchBeginEvent(const QTouchEvent* event);
|
void TouchBeginEvent(const QTouchEvent* event);
|
||||||
void TouchUpdateEvent(const QTouchEvent* event);
|
void TouchUpdateEvent(const QTouchEvent* event);
|
||||||
void TouchEndEvent();
|
void TouchEndEvent();
|
||||||
|
@ -190,7 +181,10 @@ private:
|
||||||
|
|
||||||
EmuThread* emu_thread;
|
EmuThread* emu_thread;
|
||||||
|
|
||||||
std::unique_ptr<GraphicsContext> core_context;
|
// Main context that will be shared with all other contexts that are requested.
|
||||||
|
// If this is used in a shared context setting, then this should not be used directly, but
|
||||||
|
// should instead be shared from
|
||||||
|
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
|
||||||
|
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
std::unique_ptr<QVulkanInstance> vk_instance;
|
std::unique_ptr<QVulkanInstance> vk_instance;
|
||||||
|
@ -201,12 +195,6 @@ private:
|
||||||
|
|
||||||
QByteArray geometry;
|
QByteArray geometry;
|
||||||
|
|
||||||
/// Native window handle that backs this presentation widget
|
|
||||||
QWindow* child_window = nullptr;
|
|
||||||
|
|
||||||
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
|
|
||||||
/// put the child_window into a widget then add it to the layout. This child_widget can be
|
|
||||||
/// parented to GRenderWindow and use Qt's lifetime system
|
|
||||||
QWidget* child_widget = nullptr;
|
QWidget* child_widget = nullptr;
|
||||||
|
|
||||||
bool first_frame = false;
|
bool first_frame = false;
|
||||||
|
|
|
@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Create and start the emulation thread
|
// Create and start the emulation thread
|
||||||
emu_thread = std::make_unique<EmuThread>(*render_window);
|
emu_thread = std::make_unique<EmuThread>();
|
||||||
emit EmulationStarting(emu_thread.get());
|
emit EmulationStarting(emu_thread.get());
|
||||||
emu_thread->start();
|
emu_thread->start();
|
||||||
|
|
||||||
|
@ -2378,7 +2378,6 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
// Enables the core to make the qt created contexts current on std::threads
|
// Enables the core to make the qt created contexts current on std::threads
|
||||||
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
// Qt changes the locale and causes issues in float conversion using std::to_string() when
|
// Qt changes the locale and causes issues in float conversion using std::to_string() when
|
||||||
|
|
|
@ -37,16 +37,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void MakeCurrent() override {
|
void MakeCurrent() override {
|
||||||
SDL_GL_MakeCurrent(window, context);
|
if (is_current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
is_current = SDL_GL_MakeCurrent(window, context) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoneCurrent() override {
|
void DoneCurrent() override {
|
||||||
|
if (!is_current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
SDL_GL_MakeCurrent(window, nullptr);
|
SDL_GL_MakeCurrent(window, nullptr);
|
||||||
|
is_current = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
SDL_GLContext context;
|
SDL_GLContext context;
|
||||||
|
bool is_current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||||
|
@ -148,14 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
|
||||||
SDL_GL_DeleteContext(window_context);
|
SDL_GL_DeleteContext(window_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2_GL::MakeCurrent() {
|
|
||||||
core_context->MakeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_GL::DoneCurrent() {
|
|
||||||
core_context->DoneCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||||
void* surface) const {
|
void* surface) const {
|
||||||
// Should not have been called from OpenGL
|
// Should not have been called from OpenGL
|
||||||
|
|
|
@ -13,8 +13,6 @@ public:
|
||||||
explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
|
explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
|
||||||
~EmuWindow_SDL2_GL();
|
~EmuWindow_SDL2_GL();
|
||||||
|
|
||||||
void MakeCurrent() override;
|
|
||||||
void DoneCurrent() override;
|
|
||||||
void Present() override;
|
void Present() override;
|
||||||
|
|
||||||
/// Ignored in OpenGL
|
/// Ignored in OpenGL
|
||||||
|
|
|
@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
|
||||||
vkDestroyInstance(vk_instance, nullptr);
|
vkDestroyInstance(vk_instance, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2_VK::MakeCurrent() {
|
|
||||||
// Unused on Vulkan
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_VK::DoneCurrent() {
|
|
||||||
// Unused on Vulkan
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||||
void* surface) const {
|
void* surface) const {
|
||||||
const auto instance_proc_addr = vkGetInstanceProcAddr;
|
const auto instance_proc_addr = vkGetInstanceProcAddr;
|
||||||
|
|
|
@ -13,8 +13,6 @@ public:
|
||||||
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
|
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
|
||||||
~EmuWindow_SDL2_VK();
|
~EmuWindow_SDL2_VK();
|
||||||
|
|
||||||
void MakeCurrent() override;
|
|
||||||
void DoneCurrent() override;
|
|
||||||
void Present() override;
|
void Present() override;
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||||
void* surface) const override;
|
void* surface) const override;
|
||||||
|
|
|
@ -230,17 +230,10 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
|
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
|
||||||
|
|
||||||
system.Renderer().Rasterizer().LoadDiskResources();
|
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
|
||||||
|
system.GPU().Start();
|
||||||
|
|
||||||
// Acquire render context for duration of the thread if this is the rendering thread
|
system.Renderer().Rasterizer().LoadDiskResources();
|
||||||
if (!Settings::values.use_asynchronous_gpu_emulation) {
|
|
||||||
emu_window->MakeCurrent();
|
|
||||||
}
|
|
||||||
SCOPE_EXIT({
|
|
||||||
if (!Settings::values.use_asynchronous_gpu_emulation) {
|
|
||||||
emu_window->DoneCurrent();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
std::thread render_thread([&emu_window] { emu_window->Present(); });
|
std::thread render_thread([&emu_window] { emu_window->Present(); });
|
||||||
while (emu_window->IsOpen()) {
|
while (emu_window->IsOpen()) {
|
||||||
|
|
|
@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
|
||||||
LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
|
LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
|
||||||
Common::g_scm_branch, Common::g_scm_desc);
|
Common::g_scm_branch, Common::g_scm_desc);
|
||||||
Settings::LogSettings();
|
Settings::LogSettings();
|
||||||
|
|
||||||
DoneCurrent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
|
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
|
||||||
|
@ -114,14 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
|
||||||
|
|
||||||
void EmuWindow_SDL2_Hide::PollEvents() {}
|
void EmuWindow_SDL2_Hide::PollEvents() {}
|
||||||
|
|
||||||
void EmuWindow_SDL2_Hide::MakeCurrent() {
|
|
||||||
SDL_GL_MakeCurrent(render_window, gl_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_Hide::DoneCurrent() {
|
|
||||||
SDL_GL_MakeCurrent(render_window, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EmuWindow_SDL2_Hide::IsShown() const {
|
bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||||
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
|
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
||||||
|
public:
|
||||||
|
explicit SDLGLContext() {
|
||||||
|
// create a hidden window to make the shared context against
|
||||||
|
window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
|
||||||
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
|
||||||
|
context = SDL_GL_CreateContext(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SDLGLContext() {
|
||||||
|
DoneCurrent();
|
||||||
|
SDL_GL_DeleteContext(context);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCurrent() override {
|
||||||
|
SDL_GL_MakeCurrent(window, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoneCurrent() override {
|
||||||
|
SDL_GL_MakeCurrent(window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Window* window;
|
||||||
|
SDL_GLContext context;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const {
|
||||||
|
return std::make_unique<SDLGLContext>();
|
||||||
|
}
|
||||||
|
|
|
@ -16,12 +16,6 @@ public:
|
||||||
/// Polls window events
|
/// Polls window events
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
|
|
||||||
/// Makes the graphics context current for the caller thread
|
|
||||||
void MakeCurrent() override;
|
|
||||||
|
|
||||||
/// Releases the GL context from the caller thread
|
|
||||||
void DoneCurrent() override;
|
|
||||||
|
|
||||||
/// Whether the screen is being shown or not.
|
/// Whether the screen is being shown or not.
|
||||||
bool IsShown() const override;
|
bool IsShown() const override;
|
||||||
|
|
||||||
|
@ -29,8 +23,7 @@ public:
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||||
void* surface) const override;
|
void* surface) const override;
|
||||||
|
|
||||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
bool IsOpen() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Whether the GPU and driver supports the OpenGL extension required
|
/// Whether the GPU and driver supports the OpenGL extension required
|
||||||
|
|
|
@ -164,11 +164,6 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
|
std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
|
||||||
|
|
||||||
if (!Settings::values.use_multi_core) {
|
|
||||||
// Single core mode must acquire OpenGL context for entire emulation session
|
|
||||||
emu_window->MakeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
int return_value = 0;
|
int return_value = 0;
|
||||||
const auto callback = [&finished,
|
const auto callback = [&finished,
|
||||||
|
@ -257,6 +252,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
|
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
|
||||||
|
|
||||||
|
system.GPU().Start();
|
||||||
system.Renderer().Rasterizer().LoadDiskResources();
|
system.Renderer().Rasterizer().LoadDiskResources();
|
||||||
|
|
||||||
while (!finished) {
|
while (!finished) {
|
||||||
|
|
Loading…
Reference in a new issue