summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Liam2022-11-27 20:37:37 -0500
committerGravatar Liam2022-11-28 19:49:09 -0500
commit89dd7dc1802cc53e828cb71c1f3e2bc0879459ce (patch)
tree718f8fec3534b4fdb77405e347f86bcf37980363 /src
parentMerge pull request #8829 from Docteh/qt6_0002 (diff)
downloadyuzu-89dd7dc1802cc53e828cb71c1f3e2bc0879459ce.tar.gz
yuzu-89dd7dc1802cc53e828cb71c1f3e2bc0879459ce.tar.xz
yuzu-89dd7dc1802cc53e828cb71c1f3e2bc0879459ce.zip
video_core: add null backend
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.h3
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp90
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h78
-rw-r--r--src/video_core/renderer_null/renderer_null.cpp24
-rw-r--r--src/video_core/renderer_null/renderer_null.h36
-rw-r--r--src/video_core/video_core.cpp4
-rw-r--r--src/yuzu/bootmanager.cpp18
-rw-r--r--src/yuzu/bootmanager.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp5
-rw-r--r--src/yuzu/configuration/configure_graphics.ui5
-rw-r--r--src/yuzu/main.cpp52
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu_cmd/CMakeLists.txt2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp51
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_null.h26
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
20 files changed, 383 insertions, 28 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index 00e4421f7..c0620066c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -19,6 +19,7 @@ namespace Settings {
19enum class RendererBackend : u32 { 19enum class RendererBackend : u32 {
20 OpenGL = 0, 20 OpenGL = 0,
21 Vulkan = 1, 21 Vulkan = 1,
22 Null = 2,
22}; 23};
23 24
24enum class ShaderBackend : u32 { 25enum class ShaderBackend : u32 {
@@ -411,7 +412,7 @@ struct Values {
411 412
412 // Renderer 413 // Renderer
413 SwitchableSetting<RendererBackend, true> renderer_backend{ 414 SwitchableSetting<RendererBackend, true> renderer_backend{
414 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 415 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
415 Setting<bool> renderer_debug{false, "debug"}; 416 Setting<bool> renderer_debug{false, "debug"};
416 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 417 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
417 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 418 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index abcf6eb11..8d5f2be2f 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -55,6 +55,8 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) {
55 return "OpenGL"; 55 return "OpenGL";
56 case Settings::RendererBackend::Vulkan: 56 case Settings::RendererBackend::Vulkan:
57 return "Vulkan"; 57 return "Vulkan";
58 case Settings::RendererBackend::Null:
59 return "Null";
58 } 60 }
59 return "Unknown"; 61 return "Unknown";
60} 62}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index b03a30992..0d6bf1f0d 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -91,6 +91,10 @@ add_library(video_core STATIC
91 rasterizer_interface.h 91 rasterizer_interface.h
92 renderer_base.cpp 92 renderer_base.cpp
93 renderer_base.h 93 renderer_base.h
94 renderer_null/null_rasterizer.cpp
95 renderer_null/null_rasterizer.h
96 renderer_null/renderer_null.cpp
97 renderer_null/renderer_null.h
94 renderer_opengl/gl_buffer_cache.cpp 98 renderer_opengl/gl_buffer_cache.cpp
95 renderer_opengl/gl_buffer_cache.h 99 renderer_opengl/gl_buffer_cache.h
96 renderer_opengl/gl_compute_pipeline.cpp 100 renderer_opengl/gl_compute_pipeline.cpp
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
new file mode 100644
index 000000000..9734d84bc
--- /dev/null
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -0,0 +1,90 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host1x/host1x.h"
5#include "video_core/memory_manager.h"
6#include "video_core/renderer_null/null_rasterizer.h"
7
8namespace Null {
9
10AccelerateDMA::AccelerateDMA() = default;
11
12bool AccelerateDMA::BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) {
13 return true;
14}
15bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) {
16 return true;
17}
18
19RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu)
20 : RasterizerAccelerated(cpu_memory_), m_gpu{gpu} {}
21RasterizerNull::~RasterizerNull() = default;
22
23void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {}
24void RasterizerNull::Clear(u32 layer_count) {}
25void RasterizerNull::DispatchCompute() {}
26void RasterizerNull::ResetCounter(VideoCore::QueryType type) {}
27void RasterizerNull::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
28 std::optional<u64> timestamp) {
29 if (!gpu_memory) {
30 return;
31 }
32
33 gpu_memory->Write(gpu_addr, u64{0});
34 if (timestamp) {
35 gpu_memory->Write(gpu_addr + 8, *timestamp);
36 }
37}
38void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
39 u32 size) {}
40void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {}
41void RasterizerNull::FlushAll() {}
42void RasterizerNull::FlushRegion(VAddr addr, u64 size) {}
43bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) {
44 return false;
45}
46void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {}
47void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
48void RasterizerNull::InvalidateGPUCache() {}
49void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
50void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {}
51void RasterizerNull::SignalFence(std::function<void()>&& func) {
52 func();
53}
54void RasterizerNull::SyncOperation(std::function<void()>&& func) {
55 func();
56}
57void RasterizerNull::SignalSyncPoint(u32 value) {
58 auto& syncpoint_manager = m_gpu.Host1x().GetSyncpointManager();
59 syncpoint_manager.IncrementGuest(value);
60 syncpoint_manager.IncrementHost(value);
61}
62void RasterizerNull::SignalReference() {}
63void RasterizerNull::ReleaseFences() {}
64void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {}
65void RasterizerNull::WaitForIdle() {}
66void RasterizerNull::FragmentBarrier() {}
67void RasterizerNull::TiledCacheBarrier() {}
68void RasterizerNull::FlushCommands() {}
69void RasterizerNull::TickFrame() {}
70Tegra::Engines::AccelerateDMAInterface& RasterizerNull::AccessAccelerateDMA() {
71 return m_accelerate_dma;
72}
73bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
74 const Tegra::Engines::Fermi2D::Surface& dst,
75 const Tegra::Engines::Fermi2D::Config& copy_config) {
76 return true;
77}
78void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
79 std::span<const u8> memory) {}
80bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config,
81 VAddr framebuffer_addr, u32 pixel_stride) {
82 return true;
83}
84void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
85 const VideoCore::DiskResourceLoadCallback& callback) {}
86void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {}
87void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {}
88void RasterizerNull::ReleaseChannel(s32 channel_id) {}
89
90} // namespace Null
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
new file mode 100644
index 000000000..ecf77ba42
--- /dev/null
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "video_core/control/channel_state_cache.h"
8#include "video_core/engines/maxwell_dma.h"
9#include "video_core/rasterizer_accelerated.h"
10#include "video_core/rasterizer_interface.h"
11
12namespace Core {
13class System;
14}
15
16namespace Null {
17
18class RasterizerNull;
19
20class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface {
21public:
22 explicit AccelerateDMA();
23 bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override;
24 bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override;
25};
26
27class RasterizerNull final : public VideoCore::RasterizerAccelerated,
28 protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
29public:
30 explicit RasterizerNull(Core::Memory::Memory& cpu_memory, Tegra::GPU& gpu);
31 ~RasterizerNull() override;
32
33 void Draw(bool is_indexed, u32 instance_count) override;
34 void Clear(u32 layer_count) override;
35 void DispatchCompute() override;
36 void ResetCounter(VideoCore::QueryType type) override;
37 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
38 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
39 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
40 void FlushAll() override;
41 void FlushRegion(VAddr addr, u64 size) override;
42 bool MustFlushRegion(VAddr addr, u64 size) override;
43 void InvalidateRegion(VAddr addr, u64 size) override;
44 void OnCPUWrite(VAddr addr, u64 size) override;
45 void InvalidateGPUCache() override;
46 void UnmapMemory(VAddr addr, u64 size) override;
47 void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override;
48 void SignalFence(std::function<void()>&& func) override;
49 void SyncOperation(std::function<void()>&& func) override;
50 void SignalSyncPoint(u32 value) override;
51 void SignalReference() override;
52 void ReleaseFences() override;
53 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
54 void WaitForIdle() override;
55 void FragmentBarrier() override;
56 void TiledCacheBarrier() override;
57 void FlushCommands() override;
58 void TickFrame() override;
59 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
60 const Tegra::Engines::Fermi2D::Surface& dst,
61 const Tegra::Engines::Fermi2D::Config& copy_config) override;
62 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
63 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
64 std::span<const u8> memory) override;
65 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
66 u32 pixel_stride) override;
67 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
68 const VideoCore::DiskResourceLoadCallback& callback) override;
69 void InitializeChannel(Tegra::Control::ChannelState& channel) override;
70 void BindChannel(Tegra::Control::ChannelState& channel) override;
71 void ReleaseChannel(s32 channel_id) override;
72
73private:
74 Tegra::GPU& m_gpu;
75 AccelerateDMA m_accelerate_dma;
76};
77
78} // namespace Null
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp
new file mode 100644
index 000000000..e2a189b63
--- /dev/null
+++ b/src/video_core/renderer_null/renderer_null.cpp
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/renderer_null/renderer_null.h"
5
6namespace Null {
7
8RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
9 Tegra::GPU& gpu,
10 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
11 : RendererBase(emu_window, std::move(context_)), m_gpu(gpu), m_rasterizer(cpu_memory, gpu) {}
12
13RendererNull::~RendererNull() = default;
14
15void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
16 if (!framebuffer) {
17 return;
18 }
19
20 m_gpu.RendererFrameEndNotify();
21 render_window.OnFrameDisplayed();
22}
23
24} // namespace Null
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h
new file mode 100644
index 000000000..967ff5645
--- /dev/null
+++ b/src/video_core/renderer_null/renderer_null.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <string>
8
9#include "video_core/renderer_base.h"
10#include "video_core/renderer_null/null_rasterizer.h"
11
12namespace Null {
13
14class RendererNull final : public VideoCore::RendererBase {
15public:
16 explicit RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
17 Tegra::GPU& gpu,
18 std::unique_ptr<Core::Frontend::GraphicsContext> context);
19 ~RendererNull() override;
20
21 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
22
23 VideoCore::RasterizerInterface* ReadRasterizer() override {
24 return &m_rasterizer;
25 }
26
27 [[nodiscard]] std::string GetDeviceVendor() const override {
28 return "NULL";
29 }
30
31private:
32 Tegra::GPU& m_gpu;
33 RasterizerNull m_rasterizer;
34};
35
36} // namespace Null
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 04ac4af11..fedb4a7bb 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -7,6 +7,7 @@
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "video_core/renderer_base.h" 9#include "video_core/renderer_base.h"
10#include "video_core/renderer_null/renderer_null.h"
10#include "video_core/renderer_opengl/renderer_opengl.h" 11#include "video_core/renderer_opengl/renderer_opengl.h"
11#include "video_core/renderer_vulkan/renderer_vulkan.h" 12#include "video_core/renderer_vulkan/renderer_vulkan.h"
12#include "video_core/video_core.h" 13#include "video_core/video_core.h"
@@ -26,6 +27,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
26 case Settings::RendererBackend::Vulkan: 27 case Settings::RendererBackend::Vulkan:
27 return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory, 28 return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory,
28 gpu, std::move(context)); 29 gpu, std::move(context));
30 case Settings::RendererBackend::Null:
31 return std::make_unique<Null::RendererNull>(emu_window, cpu_memory, gpu,
32 std::move(context));
29 default: 33 default:
30 return nullptr; 34 return nullptr;
31 } 35 }
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index c934069dd..f140e951f 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -237,8 +237,7 @@ private:
237 GRenderWindow* render_window; 237 GRenderWindow* render_window;
238}; 238};
239 239
240class OpenGLRenderWidget : public RenderWidget { 240struct OpenGLRenderWidget : public RenderWidget {
241public:
242 explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { 241 explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
243 windowHandle()->setSurfaceType(QWindow::OpenGLSurface); 242 windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
244 } 243 }
@@ -251,13 +250,16 @@ private:
251 std::unique_ptr<Core::Frontend::GraphicsContext> context; 250 std::unique_ptr<Core::Frontend::GraphicsContext> context;
252}; 251};
253 252
254class VulkanRenderWidget : public RenderWidget { 253struct VulkanRenderWidget : public RenderWidget {
255public:
256 explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { 254 explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
257 windowHandle()->setSurfaceType(QWindow::VulkanSurface); 255 windowHandle()->setSurfaceType(QWindow::VulkanSurface);
258 } 256 }
259}; 257};
260 258
259struct NullRenderWidget : public RenderWidget {
260 explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
261};
262
261static Core::Frontend::WindowSystemType GetWindowSystemType() { 263static Core::Frontend::WindowSystemType GetWindowSystemType() {
262 // Determine WSI type based on Qt platform. 264 // Determine WSI type based on Qt platform.
263 QString platform_name = QGuiApplication::platformName(); 265 QString platform_name = QGuiApplication::platformName();
@@ -874,6 +876,9 @@ bool GRenderWindow::InitRenderTarget() {
874 return false; 876 return false;
875 } 877 }
876 break; 878 break;
879 case Settings::RendererBackend::Null:
880 InitializeNull();
881 break;
877 } 882 }
878 883
879 // Update the Window System information with the new render target 884 // Update the Window System information with the new render target
@@ -970,6 +975,11 @@ bool GRenderWindow::InitializeVulkan() {
970 return true; 975 return true;
971} 976}
972 977
978void GRenderWindow::InitializeNull() {
979 child_widget = new NullRenderWidget(this);
980 main_context = std::make_unique<DummyContext>();
981}
982
973bool GRenderWindow::LoadOpenGL() { 983bool GRenderWindow::LoadOpenGL() {
974 auto context = CreateSharedContext(); 984 auto context = CreateSharedContext();
975 auto scope = context->Acquire(); 985 auto scope = context->Acquire();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 4a01481cd..2e19a879e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -218,6 +218,7 @@ private:
218 218
219 bool InitializeOpenGL(); 219 bool InitializeOpenGL();
220 bool InitializeVulkan(); 220 bool InitializeVulkan();
221 void InitializeNull();
221 bool LoadOpenGL(); 222 bool LoadOpenGL();
222 QStringList GetUnsupportedGLExtensions() const; 223 QStringList GetUnsupportedGLExtensions() const;
223 224
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index f1385e972..4a875f86a 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -260,6 +260,7 @@ void ConfigureGraphics::ApplyConfiguration() {
260 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); 260 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
261 switch (GetCurrentGraphicsBackend()) { 261 switch (GetCurrentGraphicsBackend()) {
262 case Settings::RendererBackend::OpenGL: 262 case Settings::RendererBackend::OpenGL:
263 case Settings::RendererBackend::Null:
263 Settings::values.shader_backend.SetGlobal(false); 264 Settings::values.shader_backend.SetGlobal(false);
264 Settings::values.vulkan_device.SetGlobal(true); 265 Settings::values.vulkan_device.SetGlobal(true);
265 Settings::values.shader_backend.SetValue(shader_backend); 266 Settings::values.shader_backend.SetValue(shader_backend);
@@ -348,6 +349,10 @@ void ConfigureGraphics::UpdateAPILayout() {
348 ui->device_widget->setVisible(true); 349 ui->device_widget->setVisible(true);
349 ui->backend_widget->setVisible(false); 350 ui->backend_widget->setVisible(false);
350 break; 351 break;
352 case Settings::RendererBackend::Null:
353 ui->device_widget->setVisible(false);
354 ui->backend_widget->setVisible(false);
355 break;
351 } 356 }
352} 357}
353 358
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 37271f956..f78396690 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -139,6 +139,11 @@
139 <string notr="true">Vulkan</string> 139 <string notr="true">Vulkan</string>
140 </property> 140 </property>
141 </item> 141 </item>
142 <item>
143 <property name="text">
144 <string>None</string>
145 </property>
146 </item>
142 </widget> 147 </widget>
143 </item> 148 </item>
144 </layout> 149 </layout>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2aae746f0..be13024c6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1007,29 +1007,11 @@ void GMainWindow::InitializeWidgets() {
1007 renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); 1007 renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
1008 renderer_status_button->setCheckable(true); 1008 renderer_status_button->setCheckable(true);
1009 renderer_status_button->setFocusPolicy(Qt::NoFocus); 1009 renderer_status_button->setFocusPolicy(Qt::NoFocus);
1010 connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) { 1010 connect(renderer_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGraphicsAPI);
1011 renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); 1011 UpdateAPIText();
1012 }); 1012 renderer_status_button->setCheckable(true);
1013 renderer_status_button->toggle();
1014
1015 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 1013 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
1016 Settings::RendererBackend::Vulkan); 1014 Settings::RendererBackend::Vulkan);
1017 connect(renderer_status_button, &QPushButton::clicked, [this] {
1018 if (emulation_running) {
1019 return;
1020 }
1021 if (renderer_status_button->isChecked()) {
1022 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
1023 } else {
1024 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
1025 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
1026 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
1027 UpdateFilterText();
1028 }
1029 }
1030
1031 system->ApplySettings();
1032 });
1033 statusBar()->insertPermanentWidget(0, renderer_status_button); 1015 statusBar()->insertPermanentWidget(0, renderer_status_button);
1034 1016
1035 statusBar()->setVisible(true); 1017 statusBar()->setVisible(true);
@@ -3248,6 +3230,18 @@ void GMainWindow::OnToggleAdaptingFilter() {
3248 UpdateFilterText(); 3230 UpdateFilterText();
3249} 3231}
3250 3232
3233void GMainWindow::OnToggleGraphicsAPI() {
3234 auto api = Settings::values.renderer_backend.GetValue();
3235 if (api == Settings::RendererBackend::OpenGL) {
3236 api = Settings::RendererBackend::Vulkan;
3237 } else {
3238 api = Settings::RendererBackend::OpenGL;
3239 }
3240 Settings::values.renderer_backend.SetValue(api);
3241 renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan);
3242 UpdateAPIText();
3243}
3244
3251void GMainWindow::OnConfigurePerGame() { 3245void GMainWindow::OnConfigurePerGame() {
3252 const u64 title_id = system->GetCurrentProcessProgramID(); 3246 const u64 title_id = system->GetCurrentProcessProgramID();
3253 OpenPerGameConfiguration(title_id, current_game_path.toStdString()); 3247 OpenPerGameConfiguration(title_id, current_game_path.toStdString());
@@ -3568,6 +3562,21 @@ void GMainWindow::UpdateDockedButton() {
3568 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD")); 3562 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
3569} 3563}
3570 3564
3565void GMainWindow::UpdateAPIText() {
3566 const auto api = Settings::values.renderer_backend.GetValue();
3567 switch (api) {
3568 case Settings::RendererBackend::OpenGL:
3569 renderer_status_button->setText(tr("OPENGL"));
3570 break;
3571 case Settings::RendererBackend::Vulkan:
3572 renderer_status_button->setText(tr("VULKAN"));
3573 break;
3574 case Settings::RendererBackend::Null:
3575 renderer_status_button->setText(tr("NULL"));
3576 break;
3577 }
3578}
3579
3571void GMainWindow::UpdateFilterText() { 3580void GMainWindow::UpdateFilterText() {
3572 const auto filter = Settings::values.scaling_filter.GetValue(); 3581 const auto filter = Settings::values.scaling_filter.GetValue();
3573 switch (filter) { 3582 switch (filter) {
@@ -3613,6 +3622,7 @@ void GMainWindow::UpdateAAText() {
3613void GMainWindow::UpdateStatusButtons() { 3622void GMainWindow::UpdateStatusButtons() {
3614 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3623 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3615 Settings::RendererBackend::Vulkan); 3624 Settings::RendererBackend::Vulkan);
3625 UpdateAPIText();
3616 UpdateGPUAccuracyButton(); 3626 UpdateGPUAccuracyButton();
3617 UpdateDockedButton(); 3627 UpdateDockedButton();
3618 UpdateFilterText(); 3628 UpdateFilterText();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6a9992d05..af6fcec3c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -307,6 +307,7 @@ private slots:
307 void OnTasStartStop(); 307 void OnTasStartStop();
308 void OnTasRecord(); 308 void OnTasRecord();
309 void OnTasReset(); 309 void OnTasReset();
310 void OnToggleGraphicsAPI();
310 void OnToggleDockedMode(); 311 void OnToggleDockedMode();
311 void OnToggleGpuAccuracy(); 312 void OnToggleGpuAccuracy();
312 void OnToggleAdaptingFilter(); 313 void OnToggleAdaptingFilter();
@@ -347,6 +348,7 @@ private:
347 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, 348 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
348 std::string_view gpu_vendor = {}); 349 std::string_view gpu_vendor = {});
349 void UpdateDockedButton(); 350 void UpdateDockedButton();
351 void UpdateAPIText();
350 void UpdateFilterText(); 352 void UpdateFilterText();
351 void UpdateAAText(); 353 void UpdateAAText();
352 void UpdateStatusBar(); 354 void UpdateStatusBar();
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 7d8ca3d8a..774d026aa 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -22,6 +22,8 @@ add_executable(yuzu-cmd
22 emu_window/emu_window_sdl2.h 22 emu_window/emu_window_sdl2.h
23 emu_window/emu_window_sdl2_gl.cpp 23 emu_window/emu_window_sdl2_gl.cpp
24 emu_window/emu_window_sdl2_gl.h 24 emu_window/emu_window_sdl2_gl.h
25 emu_window/emu_window_sdl2_null.cpp
26 emu_window/emu_window_sdl2_null.h
25 emu_window/emu_window_sdl2_vk.cpp 27 emu_window/emu_window_sdl2_vk.cpp
26 emu_window/emu_window_sdl2_vk.h 28 emu_window/emu_window_sdl2_vk.h
27 yuzu.cpp 29 yuzu.cpp
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 90bb0b415..25c23e2a5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -89,3 +89,5 @@ protected:
89 /// yuzu core instance 89 /// yuzu core instance
90 Core::System& system; 90 Core::System& system;
91}; 91};
92
93class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp
new file mode 100644
index 000000000..259192f3c
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstdlib>
5#include <memory>
6#include <string>
7
8#include <fmt/format.h>
9
10#include "common/logging/log.h"
11#include "common/scm_rev.h"
12#include "video_core/renderer_null/renderer_null.h"
13#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
14
15#ifdef YUZU_USE_EXTERNAL_SDL2
16// Include this before SDL.h to prevent the external from including a dummy
17#define USING_GENERATED_CONFIG_H
18#include <SDL_config.h>
19#endif
20
21#include <SDL.h>
22
23EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
24 Core::System& system_, bool fullscreen)
25 : EmuWindow_SDL2{input_subsystem_, system_} {
26 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
27 Common::g_scm_branch, Common::g_scm_desc);
28 render_window =
29 SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
30 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
31 SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
32
33 SetWindowIcon();
34
35 if (fullscreen) {
36 Fullscreen();
37 ShowCursor(false);
38 }
39
40 OnResize();
41 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
42 SDL_PumpEvents();
43 LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Null)", Common::g_build_name,
44 Common::g_scm_branch, Common::g_scm_desc);
45}
46
47EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default;
48
49std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Null::CreateSharedContext() const {
50 return std::make_unique<DummyContext>();
51}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h
new file mode 100644
index 000000000..35aee286d
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10
11namespace Core {
12class System;
13}
14
15namespace InputCommon {
16class InputSubsystem;
17}
18
19class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 {
20public:
21 explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
22 Core::System& system, bool fullscreen);
23 ~EmuWindow_SDL2_Null() override;
24
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
26};
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 e39ad754d..9467d164a 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -24,5 +24,3 @@ public:
24 24
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
26}; 26};
27
28class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index dfe5a30ea..a80649703 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -34,6 +34,7 @@
34#include "yuzu_cmd/config.h" 34#include "yuzu_cmd/config.h"
35#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 35#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
36#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 36#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
37#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
37#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 38#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
38 39
39#ifdef _WIN32 40#ifdef _WIN32
@@ -317,6 +318,9 @@ int main(int argc, char** argv) {
317 case Settings::RendererBackend::Vulkan: 318 case Settings::RendererBackend::Vulkan:
318 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen); 319 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
319 break; 320 break;
321 case Settings::RendererBackend::Null:
322 emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen);
323 break;
320 } 324 }
321 325
322 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 326 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());