summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/core.h6
-rw-r--r--src/core/frontend/emu_window.h3
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/core/frontend/scope_acquire_context.cpp18
-rw-r--r--src/core/frontend/scope_acquire_context.h (renamed from src/core/frontend/scope_acquire_window_context.h)10
-rw-r--r--src/core/frontend/scope_acquire_window_context.cpp18
-rw-r--r--src/core/hardware_properties.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp121
-rw-r--r--src/core/hle/kernel/kernel.h37
-rw-r--r--src/core/hle/kernel/scheduler.cpp56
-rw-r--r--src/core/hle/kernel/scheduler.h46
-rw-r--r--src/core/hle/kernel/thread.cpp12
-rw-r--r--src/core/hle/kernel/thread.h6
-rw-r--r--src/core/hle/kernel/time_manager.cpp44
-rw-r--r--src/core/hle/kernel/time_manager.h43
-rw-r--r--src/core/hle/service/am/am.cpp15
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp10
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp1
-rwxr-xr-xsrc/input_common/analog_from_button.cpp14
-rw-r--r--src/video_core/engines/maxwell_3d.h9
-rw-r--r--src/video_core/gpu.cpp65
-rw-r--r--src/video_core/gpu.h7
-rw-r--r--src/video_core/gpu_thread.cpp4
-rw-r--r--src/video_core/morton.cpp2
-rw-r--r--src/video_core/renderer_base.h10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h25
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_state.h4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp36
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp272
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h26
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp1
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp15
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp36
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h4
-rw-r--r--src/video_core/shader/decode/arithmetic.cpp33
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp94
-rw-r--r--src/video_core/surface.cpp2
-rw-r--r--src/video_core/surface.h72
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp3
-rw-r--r--src/video_core/texture_cache/surface_base.cpp4
-rw-r--r--src/video_core/texture_cache/surface_params.cpp47
-rw-r--r--src/video_core/texture_cache/surface_params.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h5
-rw-r--r--src/yuzu/bootmanager.cpp420
-rw-r--r--src/yuzu/bootmanager.h67
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp3
-rw-r--r--src/yuzu/configuration/configure_graphics.ui10
-rw-r--r--src/yuzu/main.cpp49
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h14
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp54
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h18
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h11
-rw-r--r--src/yuzu_cmd/yuzu.cpp25
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp4
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h3
72 files changed, 1402 insertions, 636 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 26612e692..54be7dc0c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -131,8 +131,8 @@ add_library(core STATIC
131 frontend/framebuffer_layout.cpp 131 frontend/framebuffer_layout.cpp
132 frontend/framebuffer_layout.h 132 frontend/framebuffer_layout.h
133 frontend/input.h 133 frontend/input.h
134 frontend/scope_acquire_window_context.cpp 134 frontend/scope_acquire_context.cpp
135 frontend/scope_acquire_window_context.h 135 frontend/scope_acquire_context.h
136 gdbstub/gdbstub.cpp 136 gdbstub/gdbstub.cpp
137 gdbstub/gdbstub.h 137 gdbstub/gdbstub.h
138 hardware_interrupt_manager.cpp 138 hardware_interrupt_manager.cpp
@@ -187,6 +187,8 @@ add_library(core STATIC
187 hle/kernel/synchronization.h 187 hle/kernel/synchronization.h
188 hle/kernel/thread.cpp 188 hle/kernel/thread.cpp
189 hle/kernel/thread.h 189 hle/kernel/thread.h
190 hle/kernel/time_manager.cpp
191 hle/kernel/time_manager.h
190 hle/kernel/transfer_memory.cpp 192 hle/kernel/transfer_memory.cpp
191 hle/kernel/transfer_memory.h 193 hle/kernel/transfer_memory.h
192 hle/kernel/vm_manager.cpp 194 hle/kernel/vm_manager.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 0eb0c0dca..a82faf127 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -24,6 +24,7 @@
24#include "core/file_sys/sdmc_factory.h" 24#include "core/file_sys/sdmc_factory.h"
25#include "core/file_sys/vfs_concat.h" 25#include "core/file_sys/vfs_concat.h"
26#include "core/file_sys/vfs_real.h" 26#include "core/file_sys/vfs_real.h"
27#include "core/frontend/scope_acquire_context.h"
27#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
28#include "core/hardware_interrupt_manager.h" 29#include "core/hardware_interrupt_manager.h"
29#include "core/hle/kernel/client_port.h" 30#include "core/hle/kernel/client_port.h"
@@ -184,6 +185,8 @@ struct System::Impl {
184 185
185 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 186 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
186 const std::string& filepath) { 187 const std::string& filepath) {
188 Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
189
187 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 190 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
188 if (!app_loader) { 191 if (!app_loader) {
189 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 192 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -707,4 +710,12 @@ const Service::SM::ServiceManager& System::ServiceManager() const {
707 return *impl->service_manager; 710 return *impl->service_manager;
708} 711}
709 712
713void System::RegisterCoreThread(std::size_t id) {
714 impl->kernel.RegisterCoreThread(id);
715}
716
717void System::RegisterHostThread() {
718 impl->kernel.RegisterHostThread();
719}
720
710} // namespace Core 721} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index e69d68fcf..8d862a8e6 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -360,6 +360,12 @@ public:
360 360
361 const CurrentBuildProcessID& GetCurrentProcessBuildID() const; 361 const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
362 362
363 /// Register a host thread as an emulated CPU Core.
364 void RegisterCoreThread(std::size_t id);
365
366 /// Register a host thread as an auxiliary thread.
367 void RegisterHostThread();
368
363private: 369private:
364 System(); 370 System();
365 371
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 3376eedc5..5eb87fb63 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -26,9 +26,6 @@ public:
26 26
27 /// Releases (dunno if this is the "right" word) the context from the caller thread 27 /// Releases (dunno if this is the "right" word) the context from the caller thread
28 virtual void DoneCurrent() = 0; 28 virtual void DoneCurrent() = 0;
29
30 /// Swap buffers to display the next frame
31 virtual void SwapBuffers() = 0;
32}; 29};
33 30
34/** 31/**
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 1d39c1faf..e9d0a40d3 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -29,6 +29,7 @@ enum class AspectRatio {
29struct FramebufferLayout { 29struct FramebufferLayout {
30 u32 width{ScreenUndocked::Width}; 30 u32 width{ScreenUndocked::Width};
31 u32 height{ScreenUndocked::Height}; 31 u32 height{ScreenUndocked::Height};
32 bool is_srgb{};
32 33
33 Common::Rectangle<u32> screen; 34 Common::Rectangle<u32> screen;
34 35
diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp
new file mode 100644
index 000000000..878c3157c
--- /dev/null
+++ b/src/core/frontend/scope_acquire_context.cpp
@@ -0,0 +1,18 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/emu_window.h"
6#include "core/frontend/scope_acquire_context.h"
7
8namespace Core::Frontend {
9
10ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
11 : context{context} {
12 context.MakeCurrent();
13}
14ScopeAcquireContext::~ScopeAcquireContext() {
15 context.DoneCurrent();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/frontend/scope_acquire_window_context.h b/src/core/frontend/scope_acquire_context.h
index 2d9f6e825..7a65c0623 100644
--- a/src/core/frontend/scope_acquire_window_context.h
+++ b/src/core/frontend/scope_acquire_context.h
@@ -8,16 +8,16 @@
8 8
9namespace Core::Frontend { 9namespace Core::Frontend {
10 10
11class EmuWindow; 11class GraphicsContext;
12 12
13/// Helper class to acquire/release window context within a given scope 13/// Helper class to acquire/release window context within a given scope
14class ScopeAcquireWindowContext : NonCopyable { 14class ScopeAcquireContext : NonCopyable {
15public: 15public:
16 explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window); 16 explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
17 ~ScopeAcquireWindowContext(); 17 ~ScopeAcquireContext();
18 18
19private: 19private:
20 Core::Frontend::EmuWindow& emu_window; 20 Core::Frontend::GraphicsContext& context;
21}; 21};
22 22
23} // namespace Core::Frontend 23} // namespace Core::Frontend
diff --git a/src/core/frontend/scope_acquire_window_context.cpp b/src/core/frontend/scope_acquire_window_context.cpp
deleted file mode 100644
index 3663dad17..000000000
--- a/src/core/frontend/scope_acquire_window_context.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/emu_window.h"
6#include "core/frontend/scope_acquire_window_context.h"
7
8namespace Core::Frontend {
9
10ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
11 : emu_window{emu_window_} {
12 emu_window.MakeCurrent();
13}
14ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
15 emu_window.DoneCurrent();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index 213461b6a..b04e046ed 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -20,6 +20,8 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
20 20
21} // namespace Hardware 21} // namespace Hardware
22 22
23constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF;
24
23struct EmuThreadHandle { 25struct EmuThreadHandle {
24 u32 host_handle; 26 u32 host_handle;
25 u32 guest_handle; 27 u32 guest_handle;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4eb1d8703..9232f4d7e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -3,9 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic> 5#include <atomic>
6#include <bitset>
6#include <functional> 7#include <functional>
7#include <memory> 8#include <memory>
8#include <mutex> 9#include <mutex>
10#include <thread>
11#include <unordered_map>
9#include <utility> 12#include <utility>
10 13
11#include "common/assert.h" 14#include "common/assert.h"
@@ -15,6 +18,7 @@
15#include "core/core.h" 18#include "core/core.h"
16#include "core/core_timing.h" 19#include "core/core_timing.h"
17#include "core/core_timing_util.h" 20#include "core/core_timing_util.h"
21#include "core/hardware_properties.h"
18#include "core/hle/kernel/client_port.h" 22#include "core/hle/kernel/client_port.h"
19#include "core/hle/kernel/errors.h" 23#include "core/hle/kernel/errors.h"
20#include "core/hle/kernel/handle_table.h" 24#include "core/hle/kernel/handle_table.h"
@@ -25,6 +29,7 @@
25#include "core/hle/kernel/scheduler.h" 29#include "core/hle/kernel/scheduler.h"
26#include "core/hle/kernel/synchronization.h" 30#include "core/hle/kernel/synchronization.h"
27#include "core/hle/kernel/thread.h" 31#include "core/hle/kernel/thread.h"
32#include "core/hle/kernel/time_manager.h"
28#include "core/hle/lock.h" 33#include "core/hle/lock.h"
29#include "core/hle/result.h" 34#include "core/hle/result.h"
30#include "core/memory.h" 35#include "core/memory.h"
@@ -44,7 +49,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
44 std::lock_guard lock{HLE::g_hle_lock}; 49 std::lock_guard lock{HLE::g_hle_lock};
45 50
46 std::shared_ptr<Thread> thread = 51 std::shared_ptr<Thread> thread =
47 system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle); 52 system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
48 if (thread == nullptr) { 53 if (thread == nullptr) {
49 LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); 54 LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
50 return; 55 return;
@@ -97,8 +102,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
97} 102}
98 103
99struct KernelCore::Impl { 104struct KernelCore::Impl {
100 explicit Impl(Core::System& system) 105 explicit Impl(Core::System& system, KernelCore& kernel)
101 : system{system}, global_scheduler{system}, synchronization{system} {} 106 : system{system}, global_scheduler{kernel}, synchronization{system}, time_manager{system} {}
102 107
103 void Initialize(KernelCore& kernel) { 108 void Initialize(KernelCore& kernel) {
104 Shutdown(); 109 Shutdown();
@@ -120,7 +125,7 @@ struct KernelCore::Impl {
120 125
121 system_resource_limit = nullptr; 126 system_resource_limit = nullptr;
122 127
123 thread_wakeup_callback_handle_table.Clear(); 128 global_handle_table.Clear();
124 thread_wakeup_event_type = nullptr; 129 thread_wakeup_event_type = nullptr;
125 preemption_event = nullptr; 130 preemption_event = nullptr;
126 131
@@ -138,8 +143,8 @@ struct KernelCore::Impl {
138 143
139 void InitializePhysicalCores() { 144 void InitializePhysicalCores() {
140 exclusive_monitor = 145 exclusive_monitor =
141 Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount()); 146 Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
142 for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) { 147 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
143 cores.emplace_back(system, i, *exclusive_monitor); 148 cores.emplace_back(system, i, *exclusive_monitor);
144 } 149 }
145 } 150 }
@@ -184,6 +189,50 @@ struct KernelCore::Impl {
184 system.Memory().SetCurrentPageTable(*process); 189 system.Memory().SetCurrentPageTable(*process);
185 } 190 }
186 191
192 void RegisterCoreThread(std::size_t core_id) {
193 std::unique_lock lock{register_thread_mutex};
194 const std::thread::id this_id = std::this_thread::get_id();
195 const auto it = host_thread_ids.find(this_id);
196 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
197 ASSERT(it == host_thread_ids.end());
198 ASSERT(!registered_core_threads[core_id]);
199 host_thread_ids[this_id] = static_cast<u32>(core_id);
200 registered_core_threads.set(core_id);
201 }
202
203 void RegisterHostThread() {
204 std::unique_lock lock{register_thread_mutex};
205 const std::thread::id this_id = std::this_thread::get_id();
206 const auto it = host_thread_ids.find(this_id);
207 ASSERT(it == host_thread_ids.end());
208 host_thread_ids[this_id] = registered_thread_ids++;
209 }
210
211 u32 GetCurrentHostThreadID() const {
212 const std::thread::id this_id = std::this_thread::get_id();
213 const auto it = host_thread_ids.find(this_id);
214 if (it == host_thread_ids.end()) {
215 return Core::INVALID_HOST_THREAD_ID;
216 }
217 return it->second;
218 }
219
220 Core::EmuThreadHandle GetCurrentEmuThreadID() const {
221 Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
222 result.host_handle = GetCurrentHostThreadID();
223 if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
224 return result;
225 }
226 const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler();
227 const Kernel::Thread* current = sched.GetCurrentThread();
228 if (current != nullptr) {
229 result.guest_handle = current->GetGlobalHandle();
230 } else {
231 result.guest_handle = InvalidHandle;
232 }
233 return result;
234 }
235
187 std::atomic<u32> next_object_id{0}; 236 std::atomic<u32> next_object_id{0};
188 std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; 237 std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
189 std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; 238 std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
@@ -194,15 +243,16 @@ struct KernelCore::Impl {
194 Process* current_process = nullptr; 243 Process* current_process = nullptr;
195 Kernel::GlobalScheduler global_scheduler; 244 Kernel::GlobalScheduler global_scheduler;
196 Kernel::Synchronization synchronization; 245 Kernel::Synchronization synchronization;
246 Kernel::TimeManager time_manager;
197 247
198 std::shared_ptr<ResourceLimit> system_resource_limit; 248 std::shared_ptr<ResourceLimit> system_resource_limit;
199 249
200 std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; 250 std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type;
201 std::shared_ptr<Core::Timing::EventType> preemption_event; 251 std::shared_ptr<Core::Timing::EventType> preemption_event;
202 252
203 // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, 253 // This is the kernel's handle table or supervisor handle table which
204 // allowing us to simply use a pool index or similar. 254 // stores all the objects in place.
205 Kernel::HandleTable thread_wakeup_callback_handle_table; 255 Kernel::HandleTable global_handle_table;
206 256
207 /// Map of named ports managed by the kernel, which can be retrieved using 257 /// Map of named ports managed by the kernel, which can be retrieved using
208 /// the ConnectToPort SVC. 258 /// the ConnectToPort SVC.
@@ -211,11 +261,17 @@ struct KernelCore::Impl {
211 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 261 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
212 std::vector<Kernel::PhysicalCore> cores; 262 std::vector<Kernel::PhysicalCore> cores;
213 263
264 // 0-3 IDs represent core threads, >3 represent others
265 std::unordered_map<std::thread::id, u32> host_thread_ids;
266 u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
267 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
268 std::mutex register_thread_mutex;
269
214 // System context 270 // System context
215 Core::System& system; 271 Core::System& system;
216}; 272};
217 273
218KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {} 274KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system, *this)} {}
219KernelCore::~KernelCore() { 275KernelCore::~KernelCore() {
220 Shutdown(); 276 Shutdown();
221} 277}
@@ -232,9 +288,8 @@ std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
232 return impl->system_resource_limit; 288 return impl->system_resource_limit;
233} 289}
234 290
235std::shared_ptr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable( 291std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
236 Handle handle) const { 292 return impl->global_handle_table.Get<Thread>(handle);
237 return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
238} 293}
239 294
240void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { 295void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
@@ -265,6 +320,14 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
265 return impl->global_scheduler; 320 return impl->global_scheduler;
266} 321}
267 322
323Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) {
324 return impl->cores[id].Scheduler();
325}
326
327const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const {
328 return impl->cores[id].Scheduler();
329}
330
268Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { 331Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
269 return impl->cores[id]; 332 return impl->cores[id];
270} 333}
@@ -281,6 +344,14 @@ const Kernel::Synchronization& KernelCore::Synchronization() const {
281 return impl->synchronization; 344 return impl->synchronization;
282} 345}
283 346
347Kernel::TimeManager& KernelCore::TimeManager() {
348 return impl->time_manager;
349}
350
351const Kernel::TimeManager& KernelCore::TimeManager() const {
352 return impl->time_manager;
353}
354
284Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { 355Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
285 return *impl->exclusive_monitor; 356 return *impl->exclusive_monitor;
286} 357}
@@ -338,12 +409,28 @@ const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallback
338 return impl->thread_wakeup_event_type; 409 return impl->thread_wakeup_event_type;
339} 410}
340 411
341Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() { 412Kernel::HandleTable& KernelCore::GlobalHandleTable() {
342 return impl->thread_wakeup_callback_handle_table; 413 return impl->global_handle_table;
414}
415
416const Kernel::HandleTable& KernelCore::GlobalHandleTable() const {
417 return impl->global_handle_table;
418}
419
420void KernelCore::RegisterCoreThread(std::size_t core_id) {
421 impl->RegisterCoreThread(core_id);
422}
423
424void KernelCore::RegisterHostThread() {
425 impl->RegisterHostThread();
426}
427
428u32 KernelCore::GetCurrentHostThreadID() const {
429 return impl->GetCurrentHostThreadID();
343} 430}
344 431
345const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const { 432Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
346 return impl->thread_wakeup_callback_handle_table; 433 return impl->GetCurrentEmuThreadID();
347} 434}
348 435
349} // namespace Kernel 436} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 1eede3063..c4f78ab71 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/object.h" 11#include "core/hle/kernel/object.h"
12 12
13namespace Core { 13namespace Core {
14struct EmuThreadHandle;
14class ExclusiveMonitor; 15class ExclusiveMonitor;
15class System; 16class System;
16} // namespace Core 17} // namespace Core
@@ -29,8 +30,10 @@ class HandleTable;
29class PhysicalCore; 30class PhysicalCore;
30class Process; 31class Process;
31class ResourceLimit; 32class ResourceLimit;
33class Scheduler;
32class Synchronization; 34class Synchronization;
33class Thread; 35class Thread;
36class TimeManager;
34 37
35/// Represents a single instance of the kernel. 38/// Represents a single instance of the kernel.
36class KernelCore { 39class KernelCore {
@@ -64,7 +67,7 @@ public:
64 std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; 67 std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
65 68
66 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. 69 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
67 std::shared_ptr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; 70 std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
68 71
69 /// Adds the given shared pointer to an internal list of active processes. 72 /// Adds the given shared pointer to an internal list of active processes.
70 void AppendNewProcess(std::shared_ptr<Process> process); 73 void AppendNewProcess(std::shared_ptr<Process> process);
@@ -87,6 +90,12 @@ public:
87 /// Gets the sole instance of the global scheduler 90 /// Gets the sole instance of the global scheduler
88 const Kernel::GlobalScheduler& GlobalScheduler() const; 91 const Kernel::GlobalScheduler& GlobalScheduler() const;
89 92
93 /// Gets the sole instance of the Scheduler assoviated with cpu core 'id'
94 Kernel::Scheduler& Scheduler(std::size_t id);
95
96 /// Gets the sole instance of the Scheduler assoviated with cpu core 'id'
97 const Kernel::Scheduler& Scheduler(std::size_t id) const;
98
90 /// Gets the an instance of the respective physical CPU core. 99 /// Gets the an instance of the respective physical CPU core.
91 Kernel::PhysicalCore& PhysicalCore(std::size_t id); 100 Kernel::PhysicalCore& PhysicalCore(std::size_t id);
92 101
@@ -99,6 +108,12 @@ public:
99 /// Gets the an instance of the Synchronization Interface. 108 /// Gets the an instance of the Synchronization Interface.
100 const Kernel::Synchronization& Synchronization() const; 109 const Kernel::Synchronization& Synchronization() const;
101 110
111 /// Gets the an instance of the TimeManager Interface.
112 Kernel::TimeManager& TimeManager();
113
114 /// Gets the an instance of the TimeManager Interface.
115 const Kernel::TimeManager& TimeManager() const;
116
102 /// Stops execution of 'id' core, in order to reschedule a new thread. 117 /// Stops execution of 'id' core, in order to reschedule a new thread.
103 void PrepareReschedule(std::size_t id); 118 void PrepareReschedule(std::size_t id);
104 119
@@ -120,6 +135,18 @@ public:
120 /// Determines whether or not the given port is a valid named port. 135 /// Determines whether or not the given port is a valid named port.
121 bool IsValidNamedPort(NamedPortTable::const_iterator port) const; 136 bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
122 137
138 /// Gets the current host_thread/guest_thread handle.
139 Core::EmuThreadHandle GetCurrentEmuThreadID() const;
140
141 /// Gets the current host_thread handle.
142 u32 GetCurrentHostThreadID() const;
143
144 /// Register the current thread as a CPU Core Thread.
145 void RegisterCoreThread(std::size_t core_id);
146
147 /// Register the current thread as a non CPU core thread.
148 void RegisterHostThread();
149
123private: 150private:
124 friend class Object; 151 friend class Object;
125 friend class Process; 152 friend class Process;
@@ -140,11 +167,11 @@ private:
140 /// Retrieves the event type used for thread wakeup callbacks. 167 /// Retrieves the event type used for thread wakeup callbacks.
141 const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; 168 const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const;
142 169
143 /// Provides a reference to the thread wakeup callback handle table. 170 /// Provides a reference to the global handle table.
144 Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); 171 Kernel::HandleTable& GlobalHandleTable();
145 172
146 /// Provides a const reference to the thread wakeup callback handle table. 173 /// Provides a const reference to the global handle table.
147 const Kernel::HandleTable& ThreadWakeupCallbackHandleTable() const; 174 const Kernel::HandleTable& GlobalHandleTable() const;
148 175
149 struct Impl; 176 struct Impl;
150 std::unique_ptr<Impl> impl; 177 std::unique_ptr<Impl> impl;
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 86f1421bf..c65f82fb7 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -18,10 +18,11 @@
18#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
19#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/scheduler.h" 20#include "core/hle/kernel/scheduler.h"
21#include "core/hle/kernel/time_manager.h"
21 22
22namespace Kernel { 23namespace Kernel {
23 24
24GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {} 25GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
25 26
26GlobalScheduler::~GlobalScheduler() = default; 27GlobalScheduler::~GlobalScheduler() = default;
27 28
@@ -35,7 +36,7 @@ void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
35} 36}
36 37
37void GlobalScheduler::UnloadThread(std::size_t core) { 38void GlobalScheduler::UnloadThread(std::size_t core) {
38 Scheduler& sched = system.Scheduler(core); 39 Scheduler& sched = kernel.Scheduler(core);
39 sched.UnloadThread(); 40 sched.UnloadThread();
40} 41}
41 42
@@ -50,7 +51,7 @@ void GlobalScheduler::SelectThread(std::size_t core) {
50 sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; 51 sched.is_context_switch_pending = sched.selected_thread != sched.current_thread;
51 std::atomic_thread_fence(std::memory_order_seq_cst); 52 std::atomic_thread_fence(std::memory_order_seq_cst);
52 }; 53 };
53 Scheduler& sched = system.Scheduler(core); 54 Scheduler& sched = kernel.Scheduler(core);
54 Thread* current_thread = nullptr; 55 Thread* current_thread = nullptr;
55 // Step 1: Get top thread in schedule queue. 56 // Step 1: Get top thread in schedule queue.
56 current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); 57 current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
@@ -356,6 +357,32 @@ void GlobalScheduler::Shutdown() {
356 thread_list.clear(); 357 thread_list.clear();
357} 358}
358 359
360void GlobalScheduler::Lock() {
361 Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID();
362 if (current_thread == current_owner) {
363 ++scope_lock;
364 } else {
365 inner_lock.lock();
366 current_owner = current_thread;
367 ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle());
368 scope_lock = 1;
369 }
370}
371
372void GlobalScheduler::Unlock() {
373 if (--scope_lock != 0) {
374 ASSERT(scope_lock > 0);
375 return;
376 }
377 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
378 SelectThread(i);
379 }
380 current_owner = Core::EmuThreadHandle::InvalidHandle();
381 scope_lock = 1;
382 inner_lock.unlock();
383 // TODO(Blinkhawk): Setup the interrupts and change context on current core.
384}
385
359Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) 386Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id)
360 : system(system), cpu_core(cpu_core), core_id(core_id) {} 387 : system(system), cpu_core(cpu_core), core_id(core_id) {}
361 388
@@ -485,4 +512,27 @@ void Scheduler::Shutdown() {
485 selected_thread = nullptr; 512 selected_thread = nullptr;
486} 513}
487 514
515SchedulerLock::SchedulerLock(KernelCore& kernel) : kernel{kernel} {
516 kernel.GlobalScheduler().Lock();
517}
518
519SchedulerLock::~SchedulerLock() {
520 kernel.GlobalScheduler().Unlock();
521}
522
523SchedulerLockAndSleep::SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle,
524 Thread* time_task, s64 nanoseconds)
525 : SchedulerLock{kernel}, event_handle{event_handle}, time_task{time_task}, nanoseconds{
526 nanoseconds} {
527 event_handle = InvalidHandle;
528}
529
530SchedulerLockAndSleep::~SchedulerLockAndSleep() {
531 if (sleep_cancelled) {
532 return;
533 }
534 auto& time_manager = kernel.TimeManager();
535 time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
536}
537
488} // namespace Kernel 538} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 96db049cb..1c93a838c 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -6,6 +6,7 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <memory> 8#include <memory>
9#include <mutex>
9#include <vector> 10#include <vector>
10 11
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -20,11 +21,13 @@ class System;
20 21
21namespace Kernel { 22namespace Kernel {
22 23
24class KernelCore;
23class Process; 25class Process;
26class SchedulerLock;
24 27
25class GlobalScheduler final { 28class GlobalScheduler final {
26public: 29public:
27 explicit GlobalScheduler(Core::System& system); 30 explicit GlobalScheduler(KernelCore& kernel);
28 ~GlobalScheduler(); 31 ~GlobalScheduler();
29 32
30 /// Adds a new thread to the scheduler 33 /// Adds a new thread to the scheduler
@@ -138,6 +141,14 @@ public:
138 void Shutdown(); 141 void Shutdown();
139 142
140private: 143private:
144 friend class SchedulerLock;
145
146 /// Lock the scheduler to the current thread.
147 void Lock();
148
149 /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
150 /// and reschedules current core if needed.
151 void Unlock();
141 /** 152 /**
142 * Transfers a thread into an specific core. If the destination_core is -1 153 * Transfers a thread into an specific core. If the destination_core is -1
143 * it will be unscheduled from its source code and added into its suggested 154 * it will be unscheduled from its source code and added into its suggested
@@ -158,9 +169,14 @@ private:
158 // ordered from Core 0 to Core 3. 169 // ordered from Core 0 to Core 3.
159 std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; 170 std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
160 171
172 /// Scheduler lock mechanisms.
173 std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock
174 std::atomic<s64> scope_lock{};
175 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
176
161 /// Lists all thread ids that aren't deleted/etc. 177 /// Lists all thread ids that aren't deleted/etc.
162 std::vector<std::shared_ptr<Thread>> thread_list; 178 std::vector<std::shared_ptr<Thread>> thread_list;
163 Core::System& system; 179 KernelCore& kernel;
164}; 180};
165 181
166class Scheduler final { 182class Scheduler final {
@@ -227,4 +243,30 @@ private:
227 bool is_context_switch_pending = false; 243 bool is_context_switch_pending = false;
228}; 244};
229 245
246class SchedulerLock {
247public:
248 explicit SchedulerLock(KernelCore& kernel);
249 ~SchedulerLock();
250
251protected:
252 KernelCore& kernel;
253};
254
255class SchedulerLockAndSleep : public SchedulerLock {
256public:
257 explicit SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* time_task,
258 s64 nanoseconds);
259 ~SchedulerLockAndSleep();
260
261 void CancelSleep() {
262 sleep_cancelled = true;
263 }
264
265private:
266 Handle& event_handle;
267 Thread* time_task;
268 s64 nanoseconds;
269 bool sleep_cancelled{};
270};
271
230} // namespace Kernel 272} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index ae5f2c8bd..bf850e0b2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -46,9 +46,9 @@ Thread::~Thread() = default;
46void Thread::Stop() { 46void Thread::Stop() {
47 // Cancel any outstanding wakeup events for this thread 47 // Cancel any outstanding wakeup events for this thread
48 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), 48 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
49 callback_handle); 49 global_handle);
50 kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); 50 kernel.GlobalHandleTable().Close(global_handle);
51 callback_handle = 0; 51 global_handle = 0;
52 SetStatus(ThreadStatus::Dead); 52 SetStatus(ThreadStatus::Dead);
53 Signal(); 53 Signal();
54 54
@@ -73,12 +73,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
73 // thread-safe version of ScheduleEvent. 73 // thread-safe version of ScheduleEvent.
74 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); 74 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
75 Core::System::GetInstance().CoreTiming().ScheduleEvent( 75 Core::System::GetInstance().CoreTiming().ScheduleEvent(
76 cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle); 76 cycles, kernel.ThreadWakeupCallbackEventType(), global_handle);
77} 77}
78 78
79void Thread::CancelWakeupTimer() { 79void Thread::CancelWakeupTimer() {
80 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), 80 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
81 callback_handle); 81 global_handle);
82} 82}
83 83
84void Thread::ResumeFromWait() { 84void Thread::ResumeFromWait() {
@@ -190,7 +190,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
190 thread->condvar_wait_address = 0; 190 thread->condvar_wait_address = 0;
191 thread->wait_handle = 0; 191 thread->wait_handle = 0;
192 thread->name = std::move(name); 192 thread->name = std::move(name);
193 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 193 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
194 thread->owner_process = &owner_process; 194 thread->owner_process = &owner_process;
195 auto& scheduler = kernel.GlobalScheduler(); 195 auto& scheduler = kernel.GlobalScheduler();
196 scheduler.AddThread(thread); 196 scheduler.AddThread(thread);
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 7a4916318..129e7858a 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -453,6 +453,10 @@ public:
453 is_sync_cancelled = value; 453 is_sync_cancelled = value;
454 } 454 }
455 455
456 Handle GetGlobalHandle() const {
457 return global_handle;
458 }
459
456private: 460private:
457 void SetSchedulingStatus(ThreadSchedStatus new_status); 461 void SetSchedulingStatus(ThreadSchedStatus new_status);
458 void SetCurrentPriority(u32 new_priority); 462 void SetCurrentPriority(u32 new_priority);
@@ -514,7 +518,7 @@ private:
514 VAddr arb_wait_address{0}; 518 VAddr arb_wait_address{0};
515 519
516 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 520 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
517 Handle callback_handle = 0; 521 Handle global_handle = 0;
518 522
519 /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread 523 /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
520 /// was waiting via WaitSynchronization then the object will be the last object that became 524 /// was waiting via WaitSynchronization then the object will be the last object that became
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
new file mode 100644
index 000000000..21b290468
--- /dev/null
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -0,0 +1,44 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9#include "core/hle/kernel/handle_table.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/thread.h"
12#include "core/hle/kernel/time_manager.h"
13
14namespace Kernel {
15
16TimeManager::TimeManager(Core::System& system) : system{system} {
17 time_manager_event_type = Core::Timing::CreateEvent(
18 "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
19 Handle proper_handle = static_cast<Handle>(thread_handle);
20 std::shared_ptr<Thread> thread =
21 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
22 thread->ResumeFromWait();
23 });
24}
25
26void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
27 if (nanoseconds > 0) {
28 ASSERT(timetask);
29 event_handle = timetask->GetGlobalHandle();
30 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
31 system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle);
32 } else {
33 event_handle = InvalidHandle;
34 }
35}
36
37void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
38 if (event_handle == InvalidHandle) {
39 return;
40 }
41 system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
42}
43
44} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
new file mode 100644
index 000000000..eaec486d1
--- /dev/null
+++ b/src/core/hle/kernel/time_manager.h
@@ -0,0 +1,43 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "core/hle/kernel/object.h"
10
11namespace Core {
12class System;
13} // namespace Core
14
15namespace Core::Timing {
16struct EventType;
17} // namespace Core::Timing
18
19namespace Kernel {
20
21class Thread;
22
23/**
24 * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
25 * method when the event is triggered.
26 */
27class TimeManager {
28public:
29 explicit TimeManager(Core::System& system);
30
31 /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
32 /// returns a non-invalid handle in `event_handle` if correctly scheduled
33 void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds);
34
35 /// Unschedule an existing time event
36 void UnscheduleTimeEvent(Handle event_handle);
37
38private:
39 Core::System& system;
40 std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
41};
42
43} // namespace Kernel
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cc978713b..d1bf13c89 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -607,7 +607,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
607 {40, nullptr, "GetCradleFwVersion"}, 607 {40, nullptr, "GetCradleFwVersion"},
608 {50, nullptr, "IsVrModeEnabled"}, 608 {50, nullptr, "IsVrModeEnabled"},
609 {51, nullptr, "SetVrModeEnabled"}, 609 {51, nullptr, "SetVrModeEnabled"},
610 {52, nullptr, "SwitchLcdBacklight"}, 610 {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
611 {53, nullptr, "BeginVrModeEx"}, 611 {53, nullptr, "BeginVrModeEx"},
612 {54, nullptr, "EndVrModeEx"}, 612 {54, nullptr, "EndVrModeEx"},
613 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 613 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
@@ -636,7 +636,6 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
636 636
637 IPC::ResponseBuilder rb{ctx, 3}; 637 IPC::ResponseBuilder rb{ctx, 3};
638 rb.Push(RESULT_SUCCESS); 638 rb.Push(RESULT_SUCCESS);
639
640 rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode 639 rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
641} 640}
642 641
@@ -660,6 +659,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
660 rb.PushEnum<AppletMessageQueue::AppletMessage>(message); 659 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
661 return; 660 return;
662 } 661 }
662
663 rb.Push(RESULT_SUCCESS); 663 rb.Push(RESULT_SUCCESS);
664 rb.PushEnum<AppletMessageQueue::AppletMessage>(message); 664 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
665} 665}
@@ -672,6 +672,17 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
672 rb.Push(static_cast<u8>(FocusState::InFocus)); 672 rb.Push(static_cast<u8>(FocusState::InFocus));
673} 673}
674 674
675void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) {
676 IPC::RequestParser rp{ctx};
677 const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
678
679 LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
680 is_lcd_backlight_off_enabled);
681
682 IPC::ResponseBuilder rb{ctx, 2};
683 rb.Push(RESULT_SUCCESS);
684}
685
675void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { 686void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
676 LOG_DEBUG(Service_AM, "called"); 687 LOG_DEBUG(Service_AM, "called");
677 688
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 0b9a4332d..0843de781 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -182,6 +182,7 @@ private:
182 void GetOperationMode(Kernel::HLERequestContext& ctx); 182 void GetOperationMode(Kernel::HLERequestContext& ctx);
183 void GetPerformanceMode(Kernel::HLERequestContext& ctx); 183 void GetPerformanceMode(Kernel::HLERequestContext& ctx);
184 void GetBootMode(Kernel::HLERequestContext& ctx); 184 void GetBootMode(Kernel::HLERequestContext& ctx);
185 void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
185 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); 186 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
186 void SetCpuBoostMode(Kernel::HLERequestContext& ctx); 187 void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
187 188
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 15c09f04c..c1e32b28c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -287,13 +287,13 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
287 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( 287 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
288 Input::AnalogDirection::DOWN)); 288 Input::AnalogDirection::DOWN));
289 289
290 pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
291 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
292 pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
293 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
294 pad_state.r_stick_right.Assign( 290 pad_state.r_stick_right.Assign(
295 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] 291 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
296 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); 292 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
293 pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
294 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
295 pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
296 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
297 pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] 297 pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
298 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); 298 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
299 299
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d1fc94060..7c0303684 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -94,6 +94,7 @@ void LogSettings() {
94 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); 94 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
95 LogSetting("Renderer_UseAsynchronousGpuEmulation", 95 LogSetting("Renderer_UseAsynchronousGpuEmulation",
96 Settings::values.use_asynchronous_gpu_emulation); 96 Settings::values.use_asynchronous_gpu_emulation);
97 LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
97 LogSetting("Audio_OutputEngine", Settings::values.sink_id); 98 LogSetting("Audio_OutputEngine", Settings::values.sink_id);
98 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 99 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
99 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 100 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
diff --git a/src/core/settings.h b/src/core/settings.h
index f837d3fbc..15b691342 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -435,6 +435,7 @@ struct Values {
435 bool use_disk_shader_cache; 435 bool use_disk_shader_cache;
436 bool use_accurate_gpu_emulation; 436 bool use_accurate_gpu_emulation;
437 bool use_asynchronous_gpu_emulation; 437 bool use_asynchronous_gpu_emulation;
438 bool use_vsync;
438 bool force_30fps_mode; 439 bool force_30fps_mode;
439 440
440 float bg_red; 441 float bg_red;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 0e72d31cd..0f3685d1c 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -188,6 +188,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
188 Settings::values.use_accurate_gpu_emulation); 188 Settings::values.use_accurate_gpu_emulation);
189 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 189 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
190 Settings::values.use_asynchronous_gpu_emulation); 190 Settings::values.use_asynchronous_gpu_emulation);
191 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
191 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 192 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
192} 193}
193 194
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index e1a260762..6cabdaa3c 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -34,6 +34,20 @@ public:
34 y * coef * (x == 0 ? 1.0f : SQRT_HALF)); 34 y * coef * (x == 0 ? 1.0f : SQRT_HALF));
35 } 35 }
36 36
37 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
38 switch (direction) {
39 case Input::AnalogDirection::RIGHT:
40 return right->GetStatus();
41 case Input::AnalogDirection::LEFT:
42 return left->GetStatus();
43 case Input::AnalogDirection::UP:
44 return up->GetStatus();
45 case Input::AnalogDirection::DOWN:
46 return down->GetStatus();
47 }
48 return false;
49 }
50
37private: 51private:
38 Button up; 52 Button up;
39 Button down; 53 Button down;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 26939be3f..6ea7cc6a5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -542,7 +542,7 @@ public:
542 BitField<12, 1, InvMemoryLayout> type; 542 BitField<12, 1, InvMemoryLayout> type;
543 } memory_layout; 543 } memory_layout;
544 union { 544 union {
545 BitField<0, 16, u32> array_mode; 545 BitField<0, 16, u32> layers;
546 BitField<16, 1, u32> volume; 546 BitField<16, 1, u32> volume;
547 }; 547 };
548 u32 layer_stride; 548 u32 layer_stride;
@@ -800,8 +800,12 @@ public:
800 800
801 u32 zeta_width; 801 u32 zeta_width;
802 u32 zeta_height; 802 u32 zeta_height;
803 union {
804 BitField<0, 16, u32> zeta_layers;
805 BitField<16, 1, u32> zeta_volume;
806 };
803 807
804 INSERT_UNION_PADDING_WORDS(0x27); 808 INSERT_UNION_PADDING_WORDS(0x26);
805 809
806 u32 depth_test_enable; 810 u32 depth_test_enable;
807 811
@@ -1507,6 +1511,7 @@ ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
1507ASSERT_REG_POSITION(rt_control, 0x487); 1511ASSERT_REG_POSITION(rt_control, 0x487);
1508ASSERT_REG_POSITION(zeta_width, 0x48a); 1512ASSERT_REG_POSITION(zeta_width, 0x48a);
1509ASSERT_REG_POSITION(zeta_height, 0x48b); 1513ASSERT_REG_POSITION(zeta_height, 0x48b);
1514ASSERT_REG_POSITION(zeta_layers, 0x48c);
1510ASSERT_REG_POSITION(depth_test_enable, 0x4B3); 1515ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
1511ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); 1516ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
1512ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); 1517ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 7d7137109..e8f763ce9 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -140,71 +140,6 @@ void GPU::FlushCommands() {
140 renderer.Rasterizer().FlushCommands(); 140 renderer.Rasterizer().FlushCommands();
141} 141}
142 142
143u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
144 ASSERT(format != RenderTargetFormat::NONE);
145
146 switch (format) {
147 case RenderTargetFormat::RGBA32_FLOAT:
148 case RenderTargetFormat::RGBA32_UINT:
149 return 16;
150 case RenderTargetFormat::RGBA16_UINT:
151 case RenderTargetFormat::RGBA16_UNORM:
152 case RenderTargetFormat::RGBA16_FLOAT:
153 case RenderTargetFormat::RGBX16_FLOAT:
154 case RenderTargetFormat::RG32_FLOAT:
155 case RenderTargetFormat::RG32_UINT:
156 return 8;
157 case RenderTargetFormat::RGBA8_UNORM:
158 case RenderTargetFormat::RGBA8_SNORM:
159 case RenderTargetFormat::RGBA8_SRGB:
160 case RenderTargetFormat::RGBA8_UINT:
161 case RenderTargetFormat::RGB10_A2_UNORM:
162 case RenderTargetFormat::BGRA8_UNORM:
163 case RenderTargetFormat::BGRA8_SRGB:
164 case RenderTargetFormat::RG16_UNORM:
165 case RenderTargetFormat::RG16_SNORM:
166 case RenderTargetFormat::RG16_UINT:
167 case RenderTargetFormat::RG16_SINT:
168 case RenderTargetFormat::RG16_FLOAT:
169 case RenderTargetFormat::R32_FLOAT:
170 case RenderTargetFormat::R11G11B10_FLOAT:
171 case RenderTargetFormat::R32_UINT:
172 return 4;
173 case RenderTargetFormat::R16_UNORM:
174 case RenderTargetFormat::R16_SNORM:
175 case RenderTargetFormat::R16_UINT:
176 case RenderTargetFormat::R16_SINT:
177 case RenderTargetFormat::R16_FLOAT:
178 case RenderTargetFormat::RG8_UNORM:
179 case RenderTargetFormat::RG8_SNORM:
180 return 2;
181 case RenderTargetFormat::R8_UNORM:
182 case RenderTargetFormat::R8_UINT:
183 return 1;
184 default:
185 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
186 return 1;
187 }
188}
189
190u32 DepthFormatBytesPerPixel(DepthFormat format) {
191 switch (format) {
192 case DepthFormat::Z32_S8_X24_FLOAT:
193 return 8;
194 case DepthFormat::Z32_FLOAT:
195 case DepthFormat::S8_Z24_UNORM:
196 case DepthFormat::Z24_X8_UNORM:
197 case DepthFormat::Z24_S8_UNORM:
198 case DepthFormat::Z24_C8_UNORM:
199 return 4;
200 case DepthFormat::Z16_UNORM:
201 return 2;
202 default:
203 UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
204 return 1;
205 }
206}
207
208// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence 143// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
209// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. 144// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
210// So the values you see in docs might be multiplied by 4. 145// So the values you see in docs might be multiplied by 4.
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 07727210c..ba8c9d665 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -57,6 +57,7 @@ enum class RenderTargetFormat : u32 {
57 RG16_UINT = 0xDD, 57 RG16_UINT = 0xDD,
58 RG16_FLOAT = 0xDE, 58 RG16_FLOAT = 0xDE,
59 R11G11B10_FLOAT = 0xE0, 59 R11G11B10_FLOAT = 0xE0,
60 R32_SINT = 0xE3,
60 R32_UINT = 0xE4, 61 R32_UINT = 0xE4,
61 R32_FLOAT = 0xE5, 62 R32_FLOAT = 0xE5,
62 B5G6R5_UNORM = 0xE8, 63 B5G6R5_UNORM = 0xE8,
@@ -82,12 +83,6 @@ enum class DepthFormat : u32 {
82 Z32_S8_X24_FLOAT = 0x19, 83 Z32_S8_X24_FLOAT = 0x19,
83}; 84};
84 85
85/// Returns the number of bytes per pixel of each rendertarget format.
86u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
87
88/// Returns the number of bytes per pixel of each depth format.
89u32 DepthFormatBytesPerPixel(DepthFormat format);
90
91struct CommandListHeader; 86struct CommandListHeader;
92class DebugContext; 87class DebugContext;
93 88
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 2cdf1aa7f..b1088af3d 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -5,7 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/scope_acquire_window_context.h" 8#include "core/frontend/scope_acquire_context.h"
9#include "video_core/dma_pusher.h" 9#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 10#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 11#include "video_core/gpu_thread.h"
@@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
27 return; 27 return;
28 } 28 }
29 29
30 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; 30 Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
31 31
32 CommandDataContainer next; 32 CommandDataContainer next;
33 while (state.is_running) { 33 while (state.is_running) {
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index 2f2fe6859..f2c83266e 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -85,6 +85,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
85 MortonCopy<true, PixelFormat::RG32UI>, 85 MortonCopy<true, PixelFormat::RG32UI>,
86 MortonCopy<true, PixelFormat::RGBX16F>, 86 MortonCopy<true, PixelFormat::RGBX16F>,
87 MortonCopy<true, PixelFormat::R32UI>, 87 MortonCopy<true, PixelFormat::R32UI>,
88 MortonCopy<true, PixelFormat::R32I>,
88 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 89 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
89 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 90 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
90 MortonCopy<true, PixelFormat::ASTC_2D_5X4>, 91 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
@@ -166,6 +167,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
166 MortonCopy<false, PixelFormat::RG32UI>, 167 MortonCopy<false, PixelFormat::RG32UI>,
167 MortonCopy<false, PixelFormat::RGBX16F>, 168 MortonCopy<false, PixelFormat::RGBX16F>,
168 MortonCopy<false, PixelFormat::R32UI>, 169 MortonCopy<false, PixelFormat::R32UI>,
170 MortonCopy<false, PixelFormat::R32I>,
169 nullptr, 171 nullptr,
170 nullptr, 172 nullptr,
171 nullptr, 173 nullptr,
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index af1bebc4f..5ec99a126 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -35,15 +35,19 @@ public:
35 explicit RendererBase(Core::Frontend::EmuWindow& window); 35 explicit RendererBase(Core::Frontend::EmuWindow& window);
36 virtual ~RendererBase(); 36 virtual ~RendererBase();
37 37
38 /// Swap buffers (render frame)
39 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
40
41 /// Initialize the renderer 38 /// Initialize the renderer
42 virtual bool Init() = 0; 39 virtual bool Init() = 0;
43 40
44 /// Shutdown the renderer 41 /// Shutdown the renderer
45 virtual void ShutDown() = 0; 42 virtual void ShutDown() = 0;
46 43
44 /// Finalize rendering the guest frame and draw into the presentation texture
45 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
46
47 /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
48 /// specific implementation)
49 virtual void TryPresent(int timeout_ms) = 0;
50
47 // Getter/setter functions: 51 // Getter/setter functions:
48 // ------------------------ 52 // ------------------------
49 53
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index f0ddfb276..c0aee770f 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -15,6 +15,24 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18void OGLRenderbuffer::Create() {
19 if (handle != 0)
20 return;
21
22 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
23 glGenRenderbuffers(1, &handle);
24}
25
26void OGLRenderbuffer::Release() {
27 if (handle == 0)
28 return;
29
30 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
31 glDeleteRenderbuffers(1, &handle);
32 OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply();
33 handle = 0;
34}
35
18void OGLTexture::Create(GLenum target) { 36void OGLTexture::Create(GLenum target) {
19 if (handle != 0) 37 if (handle != 0)
20 return; 38 return;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 514d1d165..995a4e45e 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -11,6 +11,31 @@
11 11
12namespace OpenGL { 12namespace OpenGL {
13 13
14class OGLRenderbuffer : private NonCopyable {
15public:
16 OGLRenderbuffer() = default;
17
18 OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
19
20 ~OGLRenderbuffer() {
21 Release();
22 }
23
24 OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept {
25 Release();
26 handle = std::exchange(o.handle, 0);
27 return *this;
28 }
29
30 /// Creates a new internal OpenGL resource and stores the handle
31 void Create();
32
33 /// Deletes the internal OpenGL resource
34 void Release();
35
36 GLuint handle = 0;
37};
38
14class OGLTexture : private NonCopyable { 39class OGLTexture : private NonCopyable {
15public: 40public:
16 OGLTexture() = default; 41 OGLTexture() = default;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ab1f7983c..7d3bc1a1f 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -423,6 +423,13 @@ void OpenGLState::ApplyClipControl() {
423 } 423 }
424} 424}
425 425
426void OpenGLState::ApplyRenderBuffer() {
427 if (cur_state.renderbuffer != renderbuffer) {
428 cur_state.renderbuffer = renderbuffer;
429 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
430 }
431}
432
426void OpenGLState::ApplyTextures() { 433void OpenGLState::ApplyTextures() {
427 const std::size_t size = std::size(textures); 434 const std::size_t size = std::size(textures);
428 for (std::size_t i = 0; i < size; ++i) { 435 for (std::size_t i = 0; i < size; ++i) {
@@ -478,6 +485,7 @@ void OpenGLState::Apply() {
478 ApplyPolygonOffset(); 485 ApplyPolygonOffset();
479 ApplyAlphaTest(); 486 ApplyAlphaTest();
480 ApplyClipControl(); 487 ApplyClipControl();
488 ApplyRenderBuffer();
481} 489}
482 490
483void OpenGLState::EmulateViewportWithScissor() { 491void OpenGLState::EmulateViewportWithScissor() {
@@ -551,4 +559,11 @@ OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
551 return *this; 559 return *this;
552} 560}
553 561
562OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) {
563 if (renderbuffer == handle) {
564 renderbuffer = 0;
565 }
566 return *this;
567}
568
554} // namespace OpenGL 569} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 4953eeda2..bce662f2c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -158,6 +158,8 @@ public:
158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE; 158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
159 } clip_control; 159 } clip_control;
160 160
161 GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
162
161 OpenGLState(); 163 OpenGLState();
162 164
163 /// Get the currently active OpenGL state 165 /// Get the currently active OpenGL state
@@ -196,6 +198,7 @@ public:
196 void ApplyPolygonOffset(); 198 void ApplyPolygonOffset();
197 void ApplyAlphaTest(); 199 void ApplyAlphaTest();
198 void ApplyClipControl(); 200 void ApplyClipControl();
201 void ApplyRenderBuffer();
199 202
200 /// Resets any references to the given resource 203 /// Resets any references to the given resource
201 OpenGLState& UnbindTexture(GLuint handle); 204 OpenGLState& UnbindTexture(GLuint handle);
@@ -204,6 +207,7 @@ public:
204 OpenGLState& ResetPipeline(GLuint handle); 207 OpenGLState& ResetPipeline(GLuint handle);
205 OpenGLState& ResetVertexArray(GLuint handle); 208 OpenGLState& ResetVertexArray(GLuint handle);
206 OpenGLState& ResetFramebuffer(GLuint handle); 209 OpenGLState& ResetFramebuffer(GLuint handle);
210 OpenGLState& ResetRenderbuffer(GLuint handle);
207 211
208 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test 212 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
209 void EmulateViewportWithScissor(); 213 void EmulateViewportWithScissor();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index d4b81cd87..cf934b0d8 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -87,6 +87,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format
87 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI 87 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI
88 {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F 88 {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F
89 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI 89 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI
90 {GL_R32I, GL_RED_INTEGER, GL_INT, false}, // R32I
90 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8 91 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8
91 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5 92 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5
92 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4 93 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4
@@ -260,6 +261,13 @@ CachedSurface::~CachedSurface() = default;
260void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { 261void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
261 MICROPROFILE_SCOPE(OpenGL_Texture_Download); 262 MICROPROFILE_SCOPE(OpenGL_Texture_Download);
262 263
264 if (params.IsBuffer()) {
265 glGetNamedBufferSubData(texture_buffer.handle, 0,
266 static_cast<GLsizeiptr>(params.GetHostSizeInBytes()),
267 staging_buffer.data());
268 return;
269 }
270
263 SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); 271 SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
264 272
265 for (u32 level = 0; level < params.emulated_levels; ++level) { 273 for (u32 level = 0; level < params.emulated_levels; ++level) {
@@ -398,24 +406,36 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
398CachedSurfaceView::~CachedSurfaceView() = default; 406CachedSurfaceView::~CachedSurfaceView() = default;
399 407
400void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const { 408void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const {
401 ASSERT(params.num_layers == 1 && params.num_levels == 1); 409 ASSERT(params.num_levels == 1);
402 410
403 const auto& owner_params = surface.GetSurfaceParams(); 411 const GLuint texture = surface.GetTexture();
412 if (params.num_layers > 1) {
413 // Layered framebuffer attachments
414 UNIMPLEMENTED_IF(params.base_layer != 0);
415
416 switch (params.target) {
417 case SurfaceTarget::Texture2DArray:
418 glFramebufferTexture(target, attachment, texture, params.base_level);
419 break;
420 default:
421 UNIMPLEMENTED();
422 }
423 return;
424 }
404 425
405 switch (owner_params.target) { 426 const GLenum view_target = surface.GetTarget();
427 switch (surface.GetSurfaceParams().target) {
406 case SurfaceTarget::Texture1D: 428 case SurfaceTarget::Texture1D:
407 glFramebufferTexture1D(target, attachment, surface.GetTarget(), surface.GetTexture(), 429 glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level);
408 params.base_level);
409 break; 430 break;
410 case SurfaceTarget::Texture2D: 431 case SurfaceTarget::Texture2D:
411 glFramebufferTexture2D(target, attachment, surface.GetTarget(), surface.GetTexture(), 432 glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level);
412 params.base_level);
413 break; 433 break;
414 case SurfaceTarget::Texture1DArray: 434 case SurfaceTarget::Texture1DArray:
415 case SurfaceTarget::Texture2DArray: 435 case SurfaceTarget::Texture2DArray:
416 case SurfaceTarget::TextureCubemap: 436 case SurfaceTarget::TextureCubemap:
417 case SurfaceTarget::TextureCubeArray: 437 case SurfaceTarget::TextureCubeArray:
418 glFramebufferTextureLayer(target, attachment, surface.GetTexture(), params.base_level, 438 glFramebufferTextureLayer(target, attachment, texture, params.base_level,
419 params.base_layer); 439 params.base_layer);
420 break; 440 break;
421 default: 441 default:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bba16afaf..a4340b502 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -9,11 +9,11 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/microprofile.h"
12#include "common/telemetry.h" 13#include "common/telemetry.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/core_timing.h" 15#include "core/core_timing.h"
15#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
16#include "core/frontend/scope_acquire_window_context.h"
17#include "core/memory.h" 17#include "core/memory.h"
18#include "core/perf_stats.h" 18#include "core/perf_stats.h"
19#include "core/settings.h" 19#include "core/settings.h"
@@ -24,6 +24,144 @@
24 24
25namespace OpenGL { 25namespace OpenGL {
26 26
27// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
28// to wait on available presentation frames.
29constexpr std::size_t SWAP_CHAIN_SIZE = 3;
30
31struct Frame {
32 u32 width{}; /// Width of the frame (to detect resize)
33 u32 height{}; /// Height of the frame
34 bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
35 OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
36 OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
37 OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
38 GLsync render_fence{}; /// Fence created on the render thread
39 GLsync present_fence{}; /// Fence created on the presentation thread
40 bool is_srgb{}; /// Framebuffer is sRGB or RGB
41};
42
43/**
44 * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
45 * but also make sure that rendering happens at the pace that the frontend dictates. This is a
46 * helper class that the renderer uses to sync frames between the render thread and the presentation
47 * thread
48 */
49class FrameMailbox {
50public:
51 std::mutex swap_chain_lock;
52 std::condition_variable present_cv;
53 std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
54 std::queue<Frame*> free_queue;
55 std::deque<Frame*> present_queue;
56 Frame* previous_frame{};
57
58 FrameMailbox() {
59 for (auto& frame : swap_chain) {
60 free_queue.push(&frame);
61 }
62 }
63
64 ~FrameMailbox() {
65 // lock the mutex and clear out the present and free_queues and notify any people who are
66 // blocked to prevent deadlock on shutdown
67 std::scoped_lock lock{swap_chain_lock};
68 std::queue<Frame*>().swap(free_queue);
69 present_queue.clear();
70 present_cv.notify_all();
71 }
72
73 void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
74 frame->present.Release();
75 frame->present.Create();
76 GLint previous_draw_fbo{};
77 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
78 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
79 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
80 frame->color.handle);
81 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
82 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
83 }
84 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
85 frame->color_reloaded = false;
86 }
87
88 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
89 OpenGLState prev_state = OpenGLState::GetCurState();
90 OpenGLState state = OpenGLState::GetCurState();
91
92 // Recreate the color texture attachment
93 frame->color.Release();
94 frame->color.Create();
95 state.renderbuffer = frame->color.handle;
96 state.Apply();
97 glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
98
99 // Recreate the FBO for the render target
100 frame->render.Release();
101 frame->render.Create();
102 state.draw.read_framebuffer = frame->render.handle;
103 state.draw.draw_framebuffer = frame->render.handle;
104 state.Apply();
105 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
106 frame->color.handle);
107 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
108 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
109 }
110 prev_state.Apply();
111 frame->width = width;
112 frame->height = height;
113 frame->color_reloaded = true;
114 }
115
116 Frame* GetRenderFrame() {
117 std::unique_lock lock{swap_chain_lock};
118
119 // If theres no free frames, we will reuse the oldest render frame
120 if (free_queue.empty()) {
121 auto frame = present_queue.back();
122 present_queue.pop_back();
123 return frame;
124 }
125
126 Frame* frame = free_queue.front();
127 free_queue.pop();
128 return frame;
129 }
130
131 void ReleaseRenderFrame(Frame* frame) {
132 std::unique_lock lock{swap_chain_lock};
133 present_queue.push_front(frame);
134 present_cv.notify_one();
135 }
136
137 Frame* TryGetPresentFrame(int timeout_ms) {
138 std::unique_lock lock{swap_chain_lock};
139 // wait for new entries in the present_queue
140 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
141 [&] { return !present_queue.empty(); });
142 if (present_queue.empty()) {
143 // timed out waiting for a frame to draw so return the previous frame
144 return previous_frame;
145 }
146
147 // free the previous frame and add it back to the free queue
148 if (previous_frame) {
149 free_queue.push(previous_frame);
150 }
151
152 // the newest entries are pushed to the front of the queue
153 Frame* frame = present_queue.front();
154 present_queue.pop_front();
155 // remove all old entries from the present queue and move them back to the free_queue
156 for (auto f : present_queue) {
157 free_queue.push(f);
158 }
159 present_queue.clear();
160 previous_frame = frame;
161 return frame;
162 }
163};
164
27namespace { 165namespace {
28 166
29constexpr char vertex_shader[] = R"( 167constexpr char vertex_shader[] = R"(
@@ -158,21 +296,91 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
158} // Anonymous namespace 296} // Anonymous namespace
159 297
160RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) 298RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
161 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} 299 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
300 frame_mailbox{std::make_unique<FrameMailbox>()} {}
162 301
163RendererOpenGL::~RendererOpenGL() = default; 302RendererOpenGL::~RendererOpenGL() = default;
164 303
304MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
305MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
306
165void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 307void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
308 render_window.PollEvents();
309
310 if (!framebuffer) {
311 return;
312 }
313
166 // Maintain the rasterizer's state as a priority 314 // Maintain the rasterizer's state as a priority
167 OpenGLState prev_state = OpenGLState::GetCurState(); 315 OpenGLState prev_state = OpenGLState::GetCurState();
168 state.AllDirty(); 316 state.AllDirty();
169 state.Apply(); 317 state.Apply();
170 318
319 PrepareRendertarget(framebuffer);
320 RenderScreenshot();
321
322 Frame* frame;
323 {
324 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
325
326 frame = frame_mailbox->GetRenderFrame();
327
328 // Clean up sync objects before drawing
329
330 // INTEL driver workaround. We can't delete the previous render sync object until we are
331 // sure that the presentation is done
332 if (frame->present_fence) {
333 glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
334 }
335
336 // delete the draw fence if the frame wasn't presented
337 if (frame->render_fence) {
338 glDeleteSync(frame->render_fence);
339 frame->render_fence = 0;
340 }
341
342 // wait for the presentation to be done
343 if (frame->present_fence) {
344 glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
345 glDeleteSync(frame->present_fence);
346 frame->present_fence = 0;
347 }
348 }
349
350 {
351 MICROPROFILE_SCOPE(OpenGL_RenderFrame);
352 const auto& layout = render_window.GetFramebufferLayout();
353
354 // Recreate the frame if the size of the window has changed
355 if (layout.width != frame->width || layout.height != frame->height ||
356 screen_info.display_srgb != frame->is_srgb) {
357 LOG_DEBUG(Render_OpenGL, "Reloading render frame");
358 frame->is_srgb = screen_info.display_srgb;
359 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
360 }
361 state.draw.draw_framebuffer = frame->render.handle;
362 state.Apply();
363 DrawScreen(layout);
364 // Create a fence for the frontend to wait on and swap this frame to OffTex
365 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
366 glFlush();
367 frame_mailbox->ReleaseRenderFrame(frame);
368 m_current_frame++;
369 rasterizer->TickFrame();
370 }
371
372 // Restore the rasterizer state
373 prev_state.AllDirty();
374 prev_state.Apply();
375}
376
377void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
171 if (framebuffer) { 378 if (framebuffer) {
172 // If framebuffer is provided, reload it from memory to a texture 379 // If framebuffer is provided, reload it from memory to a texture
173 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || 380 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
174 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || 381 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
175 screen_info.texture.pixel_format != framebuffer->pixel_format) { 382 screen_info.texture.pixel_format != framebuffer->pixel_format ||
383 gl_framebuffer_data.empty()) {
176 // Reallocate texture if the framebuffer size has changed. 384 // Reallocate texture if the framebuffer size has changed.
177 // This is expected to not happen very often and hence should not be a 385 // This is expected to not happen very often and hence should not be a
178 // performance problem. 386 // performance problem.
@@ -181,22 +389,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
181 389
182 // Load the framebuffer from memory, draw it to the screen, and swap buffers 390 // Load the framebuffer from memory, draw it to the screen, and swap buffers
183 LoadFBToScreenInfo(*framebuffer); 391 LoadFBToScreenInfo(*framebuffer);
184
185 if (renderer_settings.screenshot_requested)
186 CaptureScreenshot();
187
188 DrawScreen(render_window.GetFramebufferLayout());
189
190 rasterizer->TickFrame();
191
192 render_window.SwapBuffers();
193 } 392 }
194
195 render_window.PollEvents();
196
197 // Restore the rasterizer state
198 prev_state.AllDirty();
199 prev_state.Apply();
200} 393}
201 394
202void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { 395void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,13 +611,48 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
418 DrawScreenTriangles(screen_info, static_cast<float>(screen.left), 611 DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
419 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()), 612 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
420 static_cast<float>(screen.GetHeight())); 613 static_cast<float>(screen.GetHeight()));
614}
421 615
422 m_current_frame++; 616void RendererOpenGL::TryPresent(int timeout_ms) {
617 const auto& layout = render_window.GetFramebufferLayout();
618 auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
619 if (!frame) {
620 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
621 return;
622 }
623
624 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
625 // readback since we won't be doing any blending
626 glClear(GL_COLOR_BUFFER_BIT);
627
628 // Recreate the presentation FBO if the color attachment was changed
629 if (frame->color_reloaded) {
630 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
631 frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
632 }
633 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
634 // INTEL workaround.
635 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
636 // it on the emulation thread without too much penalty
637 // glDeleteSync(frame.render_sync);
638 // frame.render_sync = 0;
639
640 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
641 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
642 GL_COLOR_BUFFER_BIT, GL_LINEAR);
643
644 // Insert fence for the main thread to block on
645 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
646 glFlush();
647
648 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
423} 649}
424 650
425void RendererOpenGL::UpdateFramerate() {} 651void RendererOpenGL::RenderScreenshot() {
652 if (!renderer_settings.screenshot_requested) {
653 return;
654 }
426 655
427void RendererOpenGL::CaptureScreenshot() {
428 // Draw the current frame to the screenshot framebuffer 656 // Draw the current frame to the screenshot framebuffer
429 screenshot_framebuffer.Create(); 657 screenshot_framebuffer.Create();
430 GLuint old_read_fb = state.draw.read_framebuffer; 658 GLuint old_read_fb = state.draw.read_framebuffer;
@@ -459,8 +687,6 @@ void RendererOpenGL::CaptureScreenshot() {
459} 687}
460 688
461bool RendererOpenGL::Init() { 689bool RendererOpenGL::Init() {
462 Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window};
463
464 if (GLAD_GL_KHR_debug) { 690 if (GLAD_GL_KHR_debug) {
465 glEnable(GL_DEBUG_OUTPUT); 691 glEnable(GL_DEBUG_OUTPUT);
466 glDebugMessageCallback(DebugHandler, nullptr); 692 glDebugMessageCallback(DebugHandler, nullptr);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index b56328a7f..d45e69cbc 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -44,19 +44,23 @@ struct ScreenInfo {
44 TextureInfo texture; 44 TextureInfo texture;
45}; 45};
46 46
47struct PresentationTexture {
48 u32 width = 0;
49 u32 height = 0;
50 OGLTexture texture;
51};
52
53class FrameMailbox;
54
47class RendererOpenGL final : public VideoCore::RendererBase { 55class RendererOpenGL final : public VideoCore::RendererBase {
48public: 56public:
49 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); 57 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
50 ~RendererOpenGL() override; 58 ~RendererOpenGL() override;
51 59
52 /// Swap buffers (render frame)
53 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
54
55 /// Initialize the renderer
56 bool Init() override; 60 bool Init() override;
57
58 /// Shutdown the renderer
59 void ShutDown() override; 61 void ShutDown() override;
62 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
63 void TryPresent(int timeout_ms) override;
60 64
61private: 65private:
62 /// Initializes the OpenGL state and creates persistent objects. 66 /// Initializes the OpenGL state and creates persistent objects.
@@ -74,10 +78,7 @@ private:
74 78
75 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); 79 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
76 80
77 /// Updates the framerate. 81 void RenderScreenshot();
78 void UpdateFramerate();
79
80 void CaptureScreenshot();
81 82
82 /// Loads framebuffer from emulated memory into the active OpenGL texture. 83 /// Loads framebuffer from emulated memory into the active OpenGL texture.
83 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); 84 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
@@ -87,6 +88,8 @@ private:
87 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, 88 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
88 const TextureInfo& texture); 89 const TextureInfo& texture);
89 90
91 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
92
90 Core::Frontend::EmuWindow& emu_window; 93 Core::Frontend::EmuWindow& emu_window;
91 Core::System& system; 94 Core::System& system;
92 95
@@ -107,6 +110,9 @@ private:
107 /// Used for transforming the framebuffer orientation 110 /// Used for transforming the framebuffer orientation
108 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 111 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
109 Common::Rectangle<int> framebuffer_crop_rect; 112 Common::Rectangle<int> framebuffer_crop_rect;
113
114 /// Frame presentation mailbox
115 std::unique_ptr<FrameMailbox> frame_mailbox;
110}; 116};
111 117
112} // namespace OpenGL 118} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 5403c3ab7..ef66dd141 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -159,6 +159,7 @@ struct FormatTuple {
159 {vk::Format::eR32G32Uint, Attachable | Storage}, // RG32UI 159 {vk::Format::eR32G32Uint, Attachable | Storage}, // RG32UI
160 {vk::Format::eUndefined, {}}, // RGBX16F 160 {vk::Format::eUndefined, {}}, // RGBX16F
161 {vk::Format::eR32Uint, Attachable | Storage}, // R32UI 161 {vk::Format::eR32Uint, Attachable | Storage}, // R32UI
162 {vk::Format::eR32Sint, Attachable | Storage}, // R32I
162 {vk::Format::eAstc8x8UnormBlock, {}}, // ASTC_2D_8X8 163 {vk::Format::eAstc8x8UnormBlock, {}}, // ASTC_2D_8X8
163 {vk::Format::eUndefined, {}}, // ASTC_2D_8X5 164 {vk::Format::eUndefined, {}}, // ASTC_2D_8X5
164 {vk::Format::eUndefined, {}}, // ASTC_2D_5X4 165 {vk::Format::eUndefined, {}}, // ASTC_2D_5X4
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d5032b432..ddc62bc97 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -106,8 +106,14 @@ RendererVulkan::~RendererVulkan() {
106} 106}
107 107
108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
109 render_window.PollEvents();
110
111 if (!framebuffer) {
112 return;
113 }
114
109 const auto& layout = render_window.GetFramebufferLayout(); 115 const auto& layout = render_window.GetFramebufferLayout();
110 if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { 116 if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
111 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 117 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
112 const bool use_accelerated = 118 const bool use_accelerated =
113 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 119 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
@@ -128,13 +134,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
128 blit_screen->Recreate(); 134 blit_screen->Recreate();
129 } 135 }
130 136
131 render_window.SwapBuffers();
132 rasterizer->TickFrame(); 137 rasterizer->TickFrame();
133 } 138 }
134 139
135 render_window.PollEvents(); 140 render_window.PollEvents();
136} 141}
137 142
143void RendererVulkan::TryPresent(int /*timeout_ms*/) {
144 // TODO (bunnei): ImplementMe
145}
146
138bool RendererVulkan::Init() { 147bool RendererVulkan::Init() {
139 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; 148 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
140 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); 149 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
@@ -262,4 +271,4 @@ void RendererVulkan::Report() const {
262 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 271 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
263} 272}
264 273
265} // namespace Vulkan \ No newline at end of file 274} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index a472c5dc9..f513397f0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -36,14 +36,10 @@ public:
36 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); 36 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system);
37 ~RendererVulkan() override; 37 ~RendererVulkan() override;
38 38
39 /// Swap buffers (render frame)
40 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
41
42 /// Initialize the renderer
43 bool Init() override; 39 bool Init() override;
44
45 /// Shutdown the renderer
46 void ShutDown() override; 40 void ShutDown() override;
41 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
42 void TryPresent(int timeout_ms) override;
47 43
48private: 44private:
49 std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( 45 std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index d1da4f9d3..886bde3b9 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -523,6 +523,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
523 vk::Format::eB10G11R11UfloatPack32, 523 vk::Format::eB10G11R11UfloatPack32,
524 vk::Format::eR32Sfloat, 524 vk::Format::eR32Sfloat,
525 vk::Format::eR32Uint, 525 vk::Format::eR32Uint,
526 vk::Format::eR32Sint,
526 vk::Format::eR16Sfloat, 527 vk::Format::eR16Sfloat,
527 vk::Format::eR16G16B16A16Sfloat, 528 vk::Format::eR16G16B16A16Sfloat,
528 vk::Format::eB8G8R8A8Unorm, 529 vk::Format::eB8G8R8A8Unorm,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 31c078f6a..3bf86da87 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -611,33 +611,34 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen
611std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers( 611std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers(
612 vk::RenderPass renderpass) { 612 vk::RenderPass renderpass) {
613 FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(), 613 FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(),
614 std::numeric_limits<u32>::max()}; 614 std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
615 615
616 const auto MarkAsModifiedAndPush = [&](const View& view) { 616 const auto try_push = [&](const View& view) {
617 if (view == nullptr) { 617 if (!view) {
618 return false; 618 return false;
619 } 619 }
620 key.views.push_back(view->GetHandle()); 620 key.views.push_back(view->GetHandle());
621 key.width = std::min(key.width, view->GetWidth()); 621 key.width = std::min(key.width, view->GetWidth());
622 key.height = std::min(key.height, view->GetHeight()); 622 key.height = std::min(key.height, view->GetHeight());
623 key.layers = std::min(key.layers, view->GetNumLayers());
623 return true; 624 return true;
624 }; 625 };
625 626
626 for (std::size_t index = 0; index < std::size(color_attachments); ++index) { 627 for (std::size_t index = 0; index < std::size(color_attachments); ++index) {
627 if (MarkAsModifiedAndPush(color_attachments[index])) { 628 if (try_push(color_attachments[index])) {
628 texture_cache.MarkColorBufferInUse(index); 629 texture_cache.MarkColorBufferInUse(index);
629 } 630 }
630 } 631 }
631 if (MarkAsModifiedAndPush(zeta_attachment)) { 632 if (try_push(zeta_attachment)) {
632 texture_cache.MarkDepthBufferInUse(); 633 texture_cache.MarkDepthBufferInUse();
633 } 634 }
634 635
635 const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key); 636 const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key);
636 auto& framebuffer = fbentry->second; 637 auto& framebuffer = fbentry->second;
637 if (is_cache_miss) { 638 if (is_cache_miss) {
638 const vk::FramebufferCreateInfo framebuffer_ci({}, key.renderpass, 639 const vk::FramebufferCreateInfo framebuffer_ci(
639 static_cast<u32>(key.views.size()), 640 {}, key.renderpass, static_cast<u32>(key.views.size()), key.views.data(), key.width,
640 key.views.data(), key.width, key.height, 1); 641 key.height, key.layers);
641 const auto dev = device.GetLogical(); 642 const auto dev = device.GetLogical();
642 const auto& dld = device.GetDispatchLoader(); 643 const auto& dld = device.GetDispatchLoader();
643 framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld); 644 framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 138903d60..4dc8af6e8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -56,6 +56,7 @@ struct FramebufferCacheKey {
56 vk::RenderPass renderpass{}; 56 vk::RenderPass renderpass{};
57 u32 width = 0; 57 u32 width = 0;
58 u32 height = 0; 58 u32 height = 0;
59 u32 layers = 0;
59 ImageViewsPack views; 60 ImageViewsPack views;
60 61
61 std::size_t Hash() const noexcept { 62 std::size_t Hash() const noexcept {
@@ -66,12 +67,17 @@ struct FramebufferCacheKey {
66 } 67 }
67 boost::hash_combine(hash, width); 68 boost::hash_combine(hash, width);
68 boost::hash_combine(hash, height); 69 boost::hash_combine(hash, height);
70 boost::hash_combine(hash, layers);
69 return hash; 71 return hash;
70 } 72 }
71 73
72 bool operator==(const FramebufferCacheKey& rhs) const noexcept { 74 bool operator==(const FramebufferCacheKey& rhs) const noexcept {
73 return std::tie(renderpass, views, width, height) == 75 return std::tie(renderpass, views, width, height, layers) ==
74 std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height); 76 std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height, rhs.layers);
77 }
78
79 bool operator!=(const FramebufferCacheKey& rhs) const noexcept {
80 return !operator==(rhs);
75 } 81 }
76}; 82};
77 83
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 6d0bf6aa1..2da622d15 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -86,6 +86,7 @@ struct AttributeType {
86 86
87struct VertexIndices { 87struct VertexIndices {
88 std::optional<u32> position; 88 std::optional<u32> position;
89 std::optional<u32> layer;
89 std::optional<u32> viewport; 90 std::optional<u32> viewport;
90 std::optional<u32> point_size; 91 std::optional<u32> point_size;
91 std::optional<u32> clip_distances; 92 std::optional<u32> clip_distances;
@@ -284,9 +285,11 @@ public:
284 AddExtension("SPV_KHR_variable_pointers"); 285 AddExtension("SPV_KHR_variable_pointers");
285 AddExtension("SPV_KHR_shader_draw_parameters"); 286 AddExtension("SPV_KHR_shader_draw_parameters");
286 287
287 if (ir.UsesViewportIndex()) { 288 if (ir.UsesLayer() || ir.UsesViewportIndex()) {
288 AddCapability(spv::Capability::MultiViewport); 289 if (ir.UsesViewportIndex()) {
289 if (device.IsExtShaderViewportIndexLayerSupported()) { 290 AddCapability(spv::Capability::MultiViewport);
291 }
292 if (stage != ShaderType::Geometry && device.IsExtShaderViewportIndexLayerSupported()) {
290 AddExtension("SPV_EXT_shader_viewport_index_layer"); 293 AddExtension("SPV_EXT_shader_viewport_index_layer");
291 AddCapability(spv::Capability::ShaderViewportIndexLayerEXT); 294 AddCapability(spv::Capability::ShaderViewportIndexLayerEXT);
292 } 295 }
@@ -928,13 +931,22 @@ private:
928 VertexIndices indices; 931 VertexIndices indices;
929 indices.position = AddBuiltIn(t_float4, spv::BuiltIn::Position, "position"); 932 indices.position = AddBuiltIn(t_float4, spv::BuiltIn::Position, "position");
930 933
934 if (ir.UsesLayer()) {
935 if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) {
936 indices.layer = AddBuiltIn(t_int, spv::BuiltIn::Layer, "layer");
937 } else {
938 LOG_ERROR(
939 Render_Vulkan,
940 "Shader requires Layer but it's not supported on this stage with this device.");
941 }
942 }
943
931 if (ir.UsesViewportIndex()) { 944 if (ir.UsesViewportIndex()) {
932 if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) { 945 if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) {
933 indices.viewport = AddBuiltIn(t_int, spv::BuiltIn::ViewportIndex, "viewport_index"); 946 indices.viewport = AddBuiltIn(t_int, spv::BuiltIn::ViewportIndex, "viewport_index");
934 } else { 947 } else {
935 LOG_ERROR(Render_Vulkan, 948 LOG_ERROR(Render_Vulkan, "Shader requires ViewportIndex but it's not supported on "
936 "Shader requires ViewportIndex but it's not supported on this " 949 "this stage with this device.");
937 "stage with this device.");
938 } 950 }
939 } 951 }
940 952
@@ -1296,6 +1308,13 @@ private:
1296 } 1308 }
1297 case Attribute::Index::LayerViewportPointSize: 1309 case Attribute::Index::LayerViewportPointSize:
1298 switch (element) { 1310 switch (element) {
1311 case 1: {
1312 if (!out_indices.layer) {
1313 return {};
1314 }
1315 const u32 index = out_indices.layer.value();
1316 return {AccessElement(t_out_int, out_vertex, index), Type::Int};
1317 }
1299 case 2: { 1318 case 2: {
1300 if (!out_indices.viewport) { 1319 if (!out_indices.viewport) {
1301 return {}; 1320 return {};
@@ -1366,6 +1385,11 @@ private:
1366 UNIMPLEMENTED(); 1385 UNIMPLEMENTED();
1367 } 1386 }
1368 1387
1388 if (!target.id) {
1389 // On failure we return a nullptr target.id, skip these stores.
1390 return {};
1391 }
1392
1369 OpStore(target.id, As(Visit(src), target.type)); 1393 OpStore(target.id, As(Visit(src), target.type));
1370 return {}; 1394 return {};
1371 } 1395 }
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index d3edbe80c..22e3d34de 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -151,6 +151,10 @@ public:
151 return params.GetMipHeight(base_level); 151 return params.GetMipHeight(base_level);
152 } 152 }
153 153
154 u32 GetNumLayers() const {
155 return num_layers;
156 }
157
154 bool IsBufferView() const { 158 bool IsBufferView() const {
155 return buffer_view; 159 return buffer_view;
156 } 160 }
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index 90240c765..478394682 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -53,29 +53,24 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
53 53
54 op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b); 54 op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b);
55 55
56 // TODO(Rodrigo): Should precise be used when there's a postfactor? 56 static constexpr std::array FmulPostFactor = {
57 Node value = Operation(OperationCode::FMul, PRECISE, op_a, op_b); 57 1.000f, // None
58 0.500f, // Divide 2
59 0.250f, // Divide 4
60 0.125f, // Divide 8
61 8.000f, // Mul 8
62 4.000f, // Mul 4
63 2.000f, // Mul 2
64 };
58 65
59 if (instr.fmul.postfactor != 0) { 66 if (instr.fmul.postfactor != 0) {
60 auto postfactor = static_cast<s32>(instr.fmul.postfactor); 67 op_a = Operation(OperationCode::FMul, NO_PRECISE, op_a,
61 68 Immediate(FmulPostFactor[instr.fmul.postfactor]));
62 // Postfactor encoded as 3-bit 1's complement in instruction, interpreted with below
63 // logic.
64 if (postfactor >= 4) {
65 postfactor = 7 - postfactor;
66 } else {
67 postfactor = 0 - postfactor;
68 }
69
70 if (postfactor > 0) {
71 value = Operation(OperationCode::FMul, NO_PRECISE, value,
72 Immediate(static_cast<f32>(1 << postfactor)));
73 } else {
74 value = Operation(OperationCode::FDiv, NO_PRECISE, value,
75 Immediate(static_cast<f32>(1 << -postfactor)));
76 }
77 } 69 }
78 70
71 // TODO(Rodrigo): Should precise be used when there's a postfactor?
72 Node value = Operation(OperationCode::FMul, PRECISE, op_a, op_b);
73
79 value = GetSaturatedFloat(value, instr.alu.saturate_d); 74 value = GetSaturatedFloat(value, instr.alu.saturate_d);
80 75
81 SetInternalFlagsFromFloat(bb, value, instr.generates_cc); 76 SetInternalFlagsFromFloat(bb, value, instr.generates_cc);
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index 21366869d..2fe787d6f 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -293,44 +293,66 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
293 293
294void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c, 294void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c,
295 Node imm_lut, bool sets_cc) { 295 Node imm_lut, bool sets_cc) {
296 constexpr u32 lop_iterations = 32; 296 const Node lop3_fast = [&](const Node na, const Node nb, const Node nc, const Node ttbl) {
297 const Node one = Immediate(1); 297 Node value = Immediate(0);
298 const Node two = Immediate(2); 298 const ImmediateNode imm = std::get<ImmediateNode>(*ttbl);
299 299 if (imm.GetValue() & 0x01) {
300 Node value; 300 const Node a = Operation(OperationCode::IBitwiseNot, na);
301 for (u32 i = 0; i < lop_iterations; ++i) { 301 const Node b = Operation(OperationCode::IBitwiseNot, nb);
302 const Node shift_amount = Immediate(i); 302 const Node c = Operation(OperationCode::IBitwiseNot, nc);
303 303 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b);
304 const Node a = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_c, shift_amount); 304 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
305 const Node pack_0 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, one); 305 value = Operation(OperationCode::IBitwiseOr, value, r);
306
307 const Node b = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_b, shift_amount);
308 const Node c = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, b, one);
309 const Node pack_1 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, c, one);
310
311 const Node d = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_a, shift_amount);
312 const Node e = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, d, one);
313 const Node pack_2 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, e, two);
314
315 const Node pack_01 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_0, pack_1);
316 const Node pack_012 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_01, pack_2);
317
318 const Node shifted_bit =
319 Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, imm_lut, pack_012);
320 const Node bit = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, shifted_bit, one);
321
322 const Node right =
323 Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, bit, shift_amount);
324
325 if (i > 0) {
326 value = Operation(OperationCode::IBitwiseOr, NO_PRECISE, value, right);
327 } else {
328 value = right;
329 } 306 }
330 } 307 if (imm.GetValue() & 0x02) {
308 const Node a = Operation(OperationCode::IBitwiseNot, na);
309 const Node b = Operation(OperationCode::IBitwiseNot, nb);
310 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b);
311 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
312 value = Operation(OperationCode::IBitwiseOr, value, r);
313 }
314 if (imm.GetValue() & 0x04) {
315 const Node a = Operation(OperationCode::IBitwiseNot, na);
316 const Node c = Operation(OperationCode::IBitwiseNot, nc);
317 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb);
318 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
319 value = Operation(OperationCode::IBitwiseOr, value, r);
320 }
321 if (imm.GetValue() & 0x08) {
322 const Node a = Operation(OperationCode::IBitwiseNot, na);
323 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb);
324 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
325 value = Operation(OperationCode::IBitwiseOr, value, r);
326 }
327 if (imm.GetValue() & 0x10) {
328 const Node b = Operation(OperationCode::IBitwiseNot, nb);
329 const Node c = Operation(OperationCode::IBitwiseNot, nc);
330 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b);
331 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
332 value = Operation(OperationCode::IBitwiseOr, value, r);
333 }
334 if (imm.GetValue() & 0x20) {
335 const Node b = Operation(OperationCode::IBitwiseNot, nb);
336 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b);
337 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
338 value = Operation(OperationCode::IBitwiseOr, value, r);
339 }
340 if (imm.GetValue() & 0x40) {
341 const Node c = Operation(OperationCode::IBitwiseNot, nc);
342 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb);
343 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
344 value = Operation(OperationCode::IBitwiseOr, value, r);
345 }
346 if (imm.GetValue() & 0x80) {
347 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb);
348 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
349 value = Operation(OperationCode::IBitwiseOr, value, r);
350 }
351 return value;
352 }(op_a, op_b, op_c, imm_lut);
331 353
332 SetInternalFlagsFromInteger(bb, value, sets_cc); 354 SetInternalFlagsFromInteger(bb, lop3_fast, sets_cc);
333 SetRegister(bb, dest, value); 355 SetRegister(bb, dest, lop3_fast);
334} 356}
335 357
336} // namespace VideoCommon::Shader 358} // namespace VideoCommon::Shader
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 1655ccf16..9707c353d 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -155,6 +155,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
155 return PixelFormat::R16I; 155 return PixelFormat::R16I;
156 case Tegra::RenderTargetFormat::R32_FLOAT: 156 case Tegra::RenderTargetFormat::R32_FLOAT:
157 return PixelFormat::R32F; 157 return PixelFormat::R32F;
158 case Tegra::RenderTargetFormat::R32_SINT:
159 return PixelFormat::R32I;
158 case Tegra::RenderTargetFormat::R32_UINT: 160 case Tegra::RenderTargetFormat::R32_UINT:
159 return PixelFormat::R32UI; 161 return PixelFormat::R32UI;
160 case Tegra::RenderTargetFormat::RG32_UINT: 162 case Tegra::RenderTargetFormat::RG32_UINT:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 0d17a93ed..d88109e5a 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -59,47 +59,48 @@ enum class PixelFormat {
59 RG32UI = 41, 59 RG32UI = 41,
60 RGBX16F = 42, 60 RGBX16F = 42,
61 R32UI = 43, 61 R32UI = 43,
62 ASTC_2D_8X8 = 44, 62 R32I = 44,
63 ASTC_2D_8X5 = 45, 63 ASTC_2D_8X8 = 45,
64 ASTC_2D_5X4 = 46, 64 ASTC_2D_8X5 = 46,
65 BGRA8_SRGB = 47, 65 ASTC_2D_5X4 = 47,
66 DXT1_SRGB = 48, 66 BGRA8_SRGB = 48,
67 DXT23_SRGB = 49, 67 DXT1_SRGB = 49,
68 DXT45_SRGB = 50, 68 DXT23_SRGB = 50,
69 BC7U_SRGB = 51, 69 DXT45_SRGB = 51,
70 R4G4B4A4U = 52, 70 BC7U_SRGB = 52,
71 ASTC_2D_4X4_SRGB = 53, 71 R4G4B4A4U = 53,
72 ASTC_2D_8X8_SRGB = 54, 72 ASTC_2D_4X4_SRGB = 54,
73 ASTC_2D_8X5_SRGB = 55, 73 ASTC_2D_8X8_SRGB = 55,
74 ASTC_2D_5X4_SRGB = 56, 74 ASTC_2D_8X5_SRGB = 56,
75 ASTC_2D_5X5 = 57, 75 ASTC_2D_5X4_SRGB = 57,
76 ASTC_2D_5X5_SRGB = 58, 76 ASTC_2D_5X5 = 58,
77 ASTC_2D_10X8 = 59, 77 ASTC_2D_5X5_SRGB = 59,
78 ASTC_2D_10X8_SRGB = 60, 78 ASTC_2D_10X8 = 60,
79 ASTC_2D_6X6 = 61, 79 ASTC_2D_10X8_SRGB = 61,
80 ASTC_2D_6X6_SRGB = 62, 80 ASTC_2D_6X6 = 62,
81 ASTC_2D_10X10 = 63, 81 ASTC_2D_6X6_SRGB = 63,
82 ASTC_2D_10X10_SRGB = 64, 82 ASTC_2D_10X10 = 64,
83 ASTC_2D_12X12 = 65, 83 ASTC_2D_10X10_SRGB = 65,
84 ASTC_2D_12X12_SRGB = 66, 84 ASTC_2D_12X12 = 66,
85 ASTC_2D_8X6 = 67, 85 ASTC_2D_12X12_SRGB = 67,
86 ASTC_2D_8X6_SRGB = 68, 86 ASTC_2D_8X6 = 68,
87 ASTC_2D_6X5 = 69, 87 ASTC_2D_8X6_SRGB = 69,
88 ASTC_2D_6X5_SRGB = 70, 88 ASTC_2D_6X5 = 70,
89 E5B9G9R9F = 71, 89 ASTC_2D_6X5_SRGB = 71,
90 E5B9G9R9F = 72,
90 91
91 MaxColorFormat, 92 MaxColorFormat,
92 93
93 // Depth formats 94 // Depth formats
94 Z32F = 72, 95 Z32F = 73,
95 Z16 = 73, 96 Z16 = 74,
96 97
97 MaxDepthFormat, 98 MaxDepthFormat,
98 99
99 // DepthStencil formats 100 // DepthStencil formats
100 Z24S8 = 74, 101 Z24S8 = 75,
101 S8Z24 = 75, 102 S8Z24 = 76,
102 Z32FS8 = 76, 103 Z32FS8 = 77,
103 104
104 MaxDepthStencilFormat, 105 MaxDepthStencilFormat,
105 106
@@ -171,6 +172,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{
171 0, // RG32UI 172 0, // RG32UI
172 0, // RGBX16F 173 0, // RGBX16F
173 0, // R32UI 174 0, // R32UI
175 0, // R32I
174 2, // ASTC_2D_8X8 176 2, // ASTC_2D_8X8
175 2, // ASTC_2D_8X5 177 2, // ASTC_2D_8X5
176 2, // ASTC_2D_5X4 178 2, // ASTC_2D_5X4
@@ -267,6 +269,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
267 1, // RG32UI 269 1, // RG32UI
268 1, // RGBX16F 270 1, // RGBX16F
269 1, // R32UI 271 1, // R32UI
272 1, // R32I
270 8, // ASTC_2D_8X8 273 8, // ASTC_2D_8X8
271 8, // ASTC_2D_8X5 274 8, // ASTC_2D_8X5
272 5, // ASTC_2D_5X4 275 5, // ASTC_2D_5X4
@@ -355,6 +358,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
355 1, // RG32UI 358 1, // RG32UI
356 1, // RGBX16F 359 1, // RGBX16F
357 1, // R32UI 360 1, // R32UI
361 1, // R32I
358 8, // ASTC_2D_8X8 362 8, // ASTC_2D_8X8
359 5, // ASTC_2D_8X5 363 5, // ASTC_2D_8X5
360 4, // ASTC_2D_5X4 364 4, // ASTC_2D_5X4
@@ -443,6 +447,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
443 64, // RG32UI 447 64, // RG32UI
444 64, // RGBX16F 448 64, // RGBX16F
445 32, // R32UI 449 32, // R32UI
450 32, // R32I
446 128, // ASTC_2D_8X8 451 128, // ASTC_2D_8X8
447 128, // ASTC_2D_8X5 452 128, // ASTC_2D_8X5
448 128, // ASTC_2D_5X4 453 128, // ASTC_2D_5X4
@@ -546,6 +551,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table
546 SurfaceCompression::None, // RG32UI 551 SurfaceCompression::None, // RG32UI
547 SurfaceCompression::None, // RGBX16F 552 SurfaceCompression::None, // RGBX16F
548 SurfaceCompression::None, // R32UI 553 SurfaceCompression::None, // R32UI
554 SurfaceCompression::None, // R32I
549 SurfaceCompression::Converted, // ASTC_2D_8X8 555 SurfaceCompression::Converted, // ASTC_2D_8X8
550 SurfaceCompression::Converted, // ASTC_2D_8X5 556 SurfaceCompression::Converted, // ASTC_2D_8X5
551 SurfaceCompression::Converted, // ASTC_2D_5X4 557 SurfaceCompression::Converted, // ASTC_2D_5X4
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 81fb9f633..cc3ad8417 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -41,7 +41,7 @@ struct Table {
41 ComponentType alpha_component; 41 ComponentType alpha_component;
42 bool is_srgb; 42 bool is_srgb;
43}; 43};
44constexpr std::array<Table, 74> DefinitionTable = {{ 44constexpr std::array<Table, 75> DefinitionTable = {{
45 {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U}, 45 {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U},
46 {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S}, 46 {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S},
47 {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI}, 47 {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI},
@@ -89,6 +89,7 @@ constexpr std::array<Table, 74> DefinitionTable = {{
89 89
90 {TextureFormat::R32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32F}, 90 {TextureFormat::R32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32F},
91 {TextureFormat::R32, C, UINT, UINT, UINT, UINT, PixelFormat::R32UI}, 91 {TextureFormat::R32, C, UINT, UINT, UINT, UINT, PixelFormat::R32UI},
92 {TextureFormat::R32, C, SINT, SINT, SINT, SINT, PixelFormat::R32I},
92 93
93 {TextureFormat::E5B9G9R9_SHAREDEXP, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::E5B9G9R9F}, 94 {TextureFormat::E5B9G9R9_SHAREDEXP, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::E5B9G9R9F},
94 95
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index 84469b7ba..002df414f 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -277,6 +277,10 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
277 SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params, 277 SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params,
278 staging_buffer.data() + host_offset, level); 278 staging_buffer.data() + host_offset, level);
279 } 279 }
280 } else if (params.IsBuffer()) {
281 // Buffers don't have pitch or any fancy layout property. We can just memcpy them to guest
282 // memory.
283 std::memcpy(host_ptr, staging_buffer.data(), guest_memory_size);
280 } else { 284 } else {
281 ASSERT(params.target == SurfaceTarget::Texture2D); 285 ASSERT(params.target == SurfaceTarget::Texture2D);
282 ASSERT(params.num_levels == 1); 286 ASSERT(params.num_levels == 1);
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 38b3a4ba8..f00839313 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -84,19 +84,16 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
84 if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) { 84 if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
85 switch (params.pixel_format) { 85 switch (params.pixel_format) {
86 case PixelFormat::R16U: 86 case PixelFormat::R16U:
87 case PixelFormat::R16F: { 87 case PixelFormat::R16F:
88 params.pixel_format = PixelFormat::Z16; 88 params.pixel_format = PixelFormat::Z16;
89 break; 89 break;
90 } 90 case PixelFormat::R32F:
91 case PixelFormat::R32F: {
92 params.pixel_format = PixelFormat::Z32F; 91 params.pixel_format = PixelFormat::Z32F;
93 break; 92 break;
94 } 93 default:
95 default: {
96 UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}", 94 UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}",
97 static_cast<u32>(params.pixel_format)); 95 static_cast<u32>(params.pixel_format));
98 } 96 }
99 }
100 params.type = GetFormatType(params.pixel_format); 97 params.type = GetFormatType(params.pixel_format);
101 } 98 }
102 params.type = GetFormatType(params.pixel_format); 99 params.type = GetFormatType(params.pixel_format);
@@ -168,27 +165,29 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
168 return params; 165 return params;
169} 166}
170 167
171SurfaceParams SurfaceParams::CreateForDepthBuffer( 168SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
172 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, 169 const auto& regs = system.GPU().Maxwell3D().regs;
173 u32 block_width, u32 block_height, u32 block_depth, 170 regs.zeta_width, regs.zeta_height, regs.zeta.format, regs.zeta.memory_layout.type;
174 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
175 SurfaceParams params; 171 SurfaceParams params;
176 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 172 params.is_tiled = regs.zeta.memory_layout.type ==
173 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
177 params.srgb_conversion = false; 174 params.srgb_conversion = false;
178 params.block_width = std::min(block_width, 5U); 175 params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U);
179 params.block_height = std::min(block_height, 5U); 176 params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U);
180 params.block_depth = std::min(block_depth, 5U); 177 params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
181 params.tile_width_spacing = 1; 178 params.tile_width_spacing = 1;
182 params.pixel_format = PixelFormatFromDepthFormat(format); 179 params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
183 params.type = GetFormatType(params.pixel_format); 180 params.type = GetFormatType(params.pixel_format);
184 params.width = zeta_width; 181 params.width = regs.zeta_width;
185 params.height = zeta_height; 182 params.height = regs.zeta_height;
186 params.target = SurfaceTarget::Texture2D;
187 params.depth = 1;
188 params.pitch = 0; 183 params.pitch = 0;
189 params.num_levels = 1; 184 params.num_levels = 1;
190 params.emulated_levels = 1; 185 params.emulated_levels = 1;
191 params.is_layered = false; 186
187 const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0;
188 params.is_layered = is_layered;
189 params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
190 params.depth = is_layered ? regs.zeta_layers.Value() : 1U;
192 return params; 191 return params;
193} 192}
194 193
@@ -214,11 +213,13 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
214 params.width = params.pitch / bpp; 213 params.width = params.pitch / bpp;
215 } 214 }
216 params.height = config.height; 215 params.height = config.height;
217 params.depth = 1;
218 params.target = SurfaceTarget::Texture2D;
219 params.num_levels = 1; 216 params.num_levels = 1;
220 params.emulated_levels = 1; 217 params.emulated_levels = 1;
221 params.is_layered = false; 218
219 const bool is_layered = config.layers > 1 && params.block_depth == 0;
220 params.is_layered = is_layered;
221 params.depth = is_layered ? config.layers.Value() : 1;
222 params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
222 return params; 223 return params;
223} 224}
224 225
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
index 9256fd6d9..995cc3818 100644
--- a/src/video_core/texture_cache/surface_params.h
+++ b/src/video_core/texture_cache/surface_params.h
@@ -35,10 +35,7 @@ public:
35 const VideoCommon::Shader::Image& entry); 35 const VideoCommon::Shader::Image& entry);
36 36
37 /// Creates SurfaceCachedParams for a depth buffer configuration. 37 /// Creates SurfaceCachedParams for a depth buffer configuration.
38 static SurfaceParams CreateForDepthBuffer( 38 static SurfaceParams CreateForDepthBuffer(Core::System& system);
39 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
40 u32 block_width, u32 block_height, u32 block_depth,
41 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
42 39
43 /// Creates SurfaceCachedParams from a framebuffer configuration. 40 /// Creates SurfaceCachedParams from a framebuffer configuration.
44 static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); 41 static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 0d105d386..c70e4aec2 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -160,10 +160,7 @@ public:
160 SetEmptyDepthBuffer(); 160 SetEmptyDepthBuffer();
161 return {}; 161 return {};
162 } 162 }
163 const auto depth_params{SurfaceParams::CreateForDepthBuffer( 163 const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)};
164 system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
165 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
166 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
167 auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true); 164 auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
168 if (depth_buffer.target) 165 if (depth_buffer.target)
169 depth_buffer.target->MarkAsRenderTarget(false, NO_RT); 166 depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 55a37fffa..c3dbb1a88 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -9,6 +9,9 @@
9#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QOffscreenSurface> 11#include <QOffscreenSurface>
12#include <QOpenGLContext>
13#include <QOpenGLFunctions>
14#include <QOpenGLFunctions_4_3_Core>
12#include <QOpenGLWindow> 15#include <QOpenGLWindow>
13#include <QPainter> 16#include <QPainter>
14#include <QScreen> 17#include <QScreen>
@@ -23,9 +26,10 @@
23#include "common/assert.h" 26#include "common/assert.h"
24#include "common/microprofile.h" 27#include "common/microprofile.h"
25#include "common/scm_rev.h" 28#include "common/scm_rev.h"
29#include "common/scope_exit.h"
26#include "core/core.h" 30#include "core/core.h"
27#include "core/frontend/framebuffer_layout.h" 31#include "core/frontend/framebuffer_layout.h"
28#include "core/frontend/scope_acquire_window_context.h" 32#include "core/frontend/scope_acquire_context.h"
29#include "core/settings.h" 33#include "core/settings.h"
30#include "input_common/keyboard.h" 34#include "input_common/keyboard.h"
31#include "input_common/main.h" 35#include "input_common/main.h"
@@ -35,15 +39,27 @@
35#include "yuzu/bootmanager.h" 39#include "yuzu/bootmanager.h"
36#include "yuzu/main.h" 40#include "yuzu/main.h"
37 41
38EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 42EmuThread::EmuThread(GRenderWindow& window)
43 : shared_context{window.CreateSharedContext()},
44 context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
45 : window} {}
39 46
40EmuThread::~EmuThread() = default; 47EmuThread::~EmuThread() = default;
41 48
42void EmuThread::run() { 49static GMainWindow* GetMainWindow() {
43 render_window->MakeCurrent(); 50 for (QWidget* w : qApp->topLevelWidgets()) {
51 if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
52 return main;
53 }
54 }
55 return nullptr;
56}
44 57
58void EmuThread::run() {
45 MicroProfileOnThreadCreate("EmuThread"); 59 MicroProfileOnThreadCreate("EmuThread");
46 60
61 Core::Frontend::ScopeAcquireContext acquire_context{context};
62
47 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 63 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
48 64
49 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 65 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
@@ -53,11 +69,6 @@ void EmuThread::run() {
53 69
54 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 70 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
55 71
56 if (Settings::values.use_asynchronous_gpu_emulation) {
57 // Release OpenGL context for the GPU thread
58 render_window->DoneCurrent();
59 }
60
61 // Holds whether the cpu was running during the last iteration, 72 // Holds whether the cpu was running during the last iteration,
62 // so that the DebugModeLeft signal can be emitted before the 73 // so that the DebugModeLeft signal can be emitted before the
63 // next execution step 74 // next execution step
@@ -98,190 +109,202 @@ void EmuThread::run() {
98#if MICROPROFILE_ENABLED 109#if MICROPROFILE_ENABLED
99 MicroProfileOnThreadExit(); 110 MicroProfileOnThreadExit();
100#endif 111#endif
101
102 render_window->moveContext();
103} 112}
104 113
105class GGLContext : public Core::Frontend::GraphicsContext { 114class GGLContext : public Core::Frontend::GraphicsContext {
106public: 115public:
107 explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { 116 explicit GGLContext(QOpenGLContext* shared_context)
108 context.setFormat(shared_context->format()); 117 : context(new QOpenGLContext(shared_context->parent())),
109 context.setShareContext(shared_context); 118 surface(new QOffscreenSurface(nullptr)) {
110 context.create(); 119
120 // disable vsync for any shared contexts
121 auto format = shared_context->format();
122 format.setSwapInterval(0);
123
124 context->setShareContext(shared_context);
125 context->setFormat(format);
126 context->create();
127 surface->setParent(shared_context->parent());
128 surface->setFormat(format);
129 surface->create();
111 } 130 }
112 131
113 void MakeCurrent() override { 132 void MakeCurrent() override {
114 context.makeCurrent(shared_context->surface()); 133 context->makeCurrent(surface);
115 } 134 }
116 135
117 void DoneCurrent() override { 136 void DoneCurrent() override {
118 context.doneCurrent(); 137 context->doneCurrent();
119 } 138 }
120 139
121 void SwapBuffers() override {}
122
123private: 140private:
124 QOpenGLContext* shared_context; 141 QOpenGLContext* context;
125 QOpenGLContext context; 142 QOffscreenSurface* surface;
126}; 143};
127 144
128class GWidgetInternal : public QWindow { 145class ChildRenderWindow : public QWindow {
129public: 146public:
130 GWidgetInternal(GRenderWindow* parent) : parent(parent) {} 147 ChildRenderWindow(QWindow* parent, QWidget* event_handler)
131 virtual ~GWidgetInternal() = default; 148 : QWindow{parent}, event_handler{event_handler} {}
132 149
133 void resizeEvent(QResizeEvent* ev) override { 150 virtual ~ChildRenderWindow() = default;
134 parent->OnClientAreaResized(ev->size().width(), ev->size().height());
135 parent->OnFramebufferSizeChanged();
136 }
137 151
138 void keyPressEvent(QKeyEvent* event) override { 152 virtual void Present() = 0;
139 InputCommon::GetKeyboard()->PressKey(event->key());
140 }
141 153
142 void keyReleaseEvent(QKeyEvent* event) override { 154protected:
143 InputCommon::GetKeyboard()->ReleaseKey(event->key()); 155 bool event(QEvent* event) override {
156 switch (event->type()) {
157 case QEvent::UpdateRequest:
158 Present();
159 return true;
160 case QEvent::MouseButtonPress:
161 case QEvent::MouseButtonRelease:
162 case QEvent::MouseButtonDblClick:
163 case QEvent::MouseMove:
164 case QEvent::KeyPress:
165 case QEvent::KeyRelease:
166 case QEvent::FocusIn:
167 case QEvent::FocusOut:
168 case QEvent::FocusAboutToChange:
169 case QEvent::Enter:
170 case QEvent::Leave:
171 case QEvent::Wheel:
172 case QEvent::TabletMove:
173 case QEvent::TabletPress:
174 case QEvent::TabletRelease:
175 case QEvent::TabletEnterProximity:
176 case QEvent::TabletLeaveProximity:
177 case QEvent::TouchBegin:
178 case QEvent::TouchUpdate:
179 case QEvent::TouchEnd:
180 case QEvent::InputMethodQuery:
181 case QEvent::TouchCancel:
182 return QCoreApplication::sendEvent(event_handler, event);
183 case QEvent::Drop:
184 GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
185 return true;
186 case QEvent::DragResponse:
187 case QEvent::DragEnter:
188 case QEvent::DragLeave:
189 case QEvent::DragMove:
190 GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
191 return true;
192 default:
193 return QWindow::event(event);
194 }
144 } 195 }
145 196
146 void mousePressEvent(QMouseEvent* event) override { 197 void exposeEvent(QExposeEvent* event) override {
147 if (event->source() == Qt::MouseEventSynthesizedBySystem) 198 QWindow::requestUpdate();
148 return; // touch input is handled in TouchBeginEvent 199 QWindow::exposeEvent(event);
149
150 const auto pos{event->pos()};
151 if (event->button() == Qt::LeftButton) {
152 const auto [x, y] = parent->ScaleTouch(pos);
153 parent->TouchPressed(x, y);
154 } else if (event->button() == Qt::RightButton) {
155 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
156 }
157 } 200 }
158 201
159 void mouseMoveEvent(QMouseEvent* event) override { 202private:
160 if (event->source() == Qt::MouseEventSynthesizedBySystem) 203 QWidget* event_handler{};
161 return; // touch input is handled in TouchUpdateEvent 204};
162 205
163 const auto pos{event->pos()}; 206class OpenGLWindow final : public ChildRenderWindow {
164 const auto [x, y] = parent->ScaleTouch(pos); 207public:
165 parent->TouchMoved(x, y); 208 OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
166 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 209 : ChildRenderWindow{parent, event_handler},
167 } 210 context(new QOpenGLContext(shared_context->parent())) {
168 211
169 void mouseReleaseEvent(QMouseEvent* event) override { 212 // disable vsync for any shared contexts
170 if (event->source() == Qt::MouseEventSynthesizedBySystem) 213 auto format = shared_context->format();
171 return; // touch input is handled in TouchEndEvent 214 format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
215 this->setFormat(format);
172 216
173 if (event->button() == Qt::LeftButton) 217 context->setShareContext(shared_context);
174 parent->TouchReleased(); 218 context->setScreen(this->screen());
175 else if (event->button() == Qt::RightButton) 219 context->setFormat(format);
176 InputCommon::GetMotionEmu()->EndTilt(); 220 context->create();
177 }
178 221
179 void DisablePainting() { 222 setSurfaceType(QWindow::OpenGLSurface);
180 do_painting = false;
181 }
182 223
183 void EnablePainting() { 224 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
184 do_painting = true; 225 // WA_DontShowOnScreen, WA_DeleteOnClose
185 } 226 }
186 227
187 std::pair<unsigned, unsigned> GetSize() const { 228 ~OpenGLWindow() override {
188 return std::make_pair(width(), height()); 229 context->doneCurrent();
189 } 230 }
190 231
191protected: 232 void Present() override {
192 bool IsPaintingEnabled() const { 233 if (!isExposed()) {
193 return do_painting; 234 return;
235 }
236
237 context->makeCurrent(this);
238 Core::System::GetInstance().Renderer().TryPresent(100);
239 context->swapBuffers(this);
240 auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
241 f->glFinish();
242 QWindow::requestUpdate();
194 } 243 }
195 244
196private: 245private:
197 GRenderWindow* parent; 246 QOpenGLContext* context{};
198 bool do_painting = false;
199};
200
201// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
202// context.
203// The corresponding functionality is handled in EmuThread instead
204class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow {
205public:
206 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
207 : GWidgetInternal(parent), QOpenGLWindow(shared_context) {}
208 ~GGLWidgetInternal() override = default;
209
210 void paintEvent(QPaintEvent* ev) override {
211 if (IsPaintingEnabled()) {
212 QPainter painter(this);
213 }
214 }
215}; 247};
216 248
217#ifdef HAS_VULKAN 249#ifdef HAS_VULKAN
218class GVKWidgetInternal final : public GWidgetInternal { 250class VulkanWindow final : public ChildRenderWindow {
219public: 251public:
220 GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { 252 VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance)
253 : ChildRenderWindow{parent, event_handler} {
221 setSurfaceType(QSurface::SurfaceType::VulkanSurface); 254 setSurfaceType(QSurface::SurfaceType::VulkanSurface);
222 setVulkanInstance(instance); 255 setVulkanInstance(instance);
223 } 256 }
224 ~GVKWidgetInternal() override = default; 257
258 ~VulkanWindow() override = default;
259
260 void Present() override {
261 // TODO(bunnei): ImplementMe
262 }
263
264private:
265 QWidget* event_handler{};
225}; 266};
226#endif 267#endif
227 268
228GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) 269GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
229 : QWidget(parent), emu_thread(emu_thread) { 270 : QWidget(parent_), emu_thread(emu_thread) {
230 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 271 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
231 .arg(QString::fromUtf8(Common::g_build_name), 272 .arg(QString::fromUtf8(Common::g_build_name),
232 QString::fromUtf8(Common::g_scm_branch), 273 QString::fromUtf8(Common::g_scm_branch),
233 QString::fromUtf8(Common::g_scm_desc))); 274 QString::fromUtf8(Common::g_scm_desc)));
234 setAttribute(Qt::WA_AcceptTouchEvents); 275 setAttribute(Qt::WA_AcceptTouchEvents);
235 276 auto layout = new QHBoxLayout(this);
277 layout->setMargin(0);
278 setLayout(layout);
236 InputCommon::Init(); 279 InputCommon::Init();
280
281 GMainWindow* parent = GetMainWindow();
237 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 282 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
238} 283}
239 284
240GRenderWindow::~GRenderWindow() { 285GRenderWindow::~GRenderWindow() {
241 InputCommon::Shutdown(); 286 InputCommon::Shutdown();
242
243 // Avoid an unordered destruction that generates a segfault
244 delete child;
245} 287}
246 288
247void GRenderWindow::moveContext() { 289void GRenderWindow::MakeCurrent() {
248 if (!context) { 290 if (core_context) {
249 return; 291 core_context->MakeCurrent();
250 } 292 }
251 DoneCurrent();
252
253 // If the thread started running, move the GL Context to the new thread. Otherwise, move it
254 // back.
255 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
256 ? emu_thread
257 : qApp->thread();
258 context->moveToThread(thread);
259} 293}
260 294
261void GRenderWindow::SwapBuffers() { 295void GRenderWindow::DoneCurrent() {
262 if (context) { 296 if (core_context) {
263 context->swapBuffers(child); 297 core_context->DoneCurrent();
264 } 298 }
299}
300
301void GRenderWindow::PollEvents() {
265 if (!first_frame) { 302 if (!first_frame) {
266 first_frame = true; 303 first_frame = true;
267 emit FirstFrameDisplayed(); 304 emit FirstFrameDisplayed();
268 } 305 }
269} 306}
270 307
271void GRenderWindow::MakeCurrent() {
272 if (context) {
273 context->makeCurrent(child);
274 }
275}
276
277void GRenderWindow::DoneCurrent() {
278 if (context) {
279 context->doneCurrent();
280 }
281}
282
283void GRenderWindow::PollEvents() {}
284
285bool GRenderWindow::IsShown() const { 308bool GRenderWindow::IsShown() const {
286 return !isMinimized(); 309 return !isMinimized();
287} 310}
@@ -291,7 +314,7 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i
291#ifdef HAS_VULKAN 314#ifdef HAS_VULKAN
292 const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); 315 const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
293 const VkInstance instance_copy = vk_instance->vkInstance(); 316 const VkInstance instance_copy = vk_instance->vkInstance();
294 const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); 317 const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
295 318
296 std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); 319 std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
297 std::memcpy(instance, &instance_copy, sizeof(instance_copy)); 320 std::memcpy(instance, &instance_copy, sizeof(instance_copy));
@@ -309,21 +332,10 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i
309void GRenderWindow::OnFramebufferSizeChanged() { 332void GRenderWindow::OnFramebufferSizeChanged() {
310 // Screen changes potentially incur a change in screen DPI, hence we should update the 333 // Screen changes potentially incur a change in screen DPI, hence we should update the
311 // framebuffer size 334 // framebuffer size
312 const qreal pixelRatio{GetWindowPixelRatio()}; 335 const qreal pixel_ratio = windowPixelRatio();
313 const auto size{child->GetSize()}; 336 const u32 width = this->width() * pixel_ratio;
314 UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); 337 const u32 height = this->height() * pixel_ratio;
315} 338 UpdateCurrentFramebufferLayout(width, height);
316
317void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
318 if (child) {
319 child->keyPressEvent(event);
320 }
321}
322
323void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
324 if (child) {
325 child->keyReleaseEvent(event);
326 }
327} 339}
328 340
329void GRenderWindow::BackupGeometry() { 341void GRenderWindow::BackupGeometry() {
@@ -351,13 +363,12 @@ QByteArray GRenderWindow::saveGeometry() {
351 return geometry; 363 return geometry;
352} 364}
353 365
354qreal GRenderWindow::GetWindowPixelRatio() const { 366qreal GRenderWindow::windowPixelRatio() const {
355 // windowHandle() might not be accessible until the window is displayed to screen. 367 return devicePixelRatio();
356 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
357} 368}
358 369
359std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { 370std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
360 const qreal pixel_ratio{GetWindowPixelRatio()}; 371 const qreal pixel_ratio = windowPixelRatio();
361 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), 372 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
362 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; 373 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
363} 374}
@@ -367,6 +378,47 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
367 QWidget::closeEvent(event); 378 QWidget::closeEvent(event);
368} 379}
369 380
381void GRenderWindow::keyPressEvent(QKeyEvent* event) {
382 InputCommon::GetKeyboard()->PressKey(event->key());
383}
384
385void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
386 InputCommon::GetKeyboard()->ReleaseKey(event->key());
387}
388
389void GRenderWindow::mousePressEvent(QMouseEvent* event) {
390 if (event->source() == Qt::MouseEventSynthesizedBySystem)
391 return; // touch input is handled in TouchBeginEvent
392
393 auto pos = event->pos();
394 if (event->button() == Qt::LeftButton) {
395 const auto [x, y] = ScaleTouch(pos);
396 this->TouchPressed(x, y);
397 } else if (event->button() == Qt::RightButton) {
398 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
399 }
400}
401
402void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
403 if (event->source() == Qt::MouseEventSynthesizedBySystem)
404 return; // touch input is handled in TouchUpdateEvent
405
406 auto pos = event->pos();
407 const auto [x, y] = ScaleTouch(pos);
408 this->TouchMoved(x, y);
409 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
410}
411
412void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
413 if (event->source() == Qt::MouseEventSynthesizedBySystem)
414 return; // touch input is handled in TouchEndEvent
415
416 if (event->button() == Qt::LeftButton)
417 this->TouchReleased();
418 else if (event->button() == Qt::RightButton)
419 InputCommon::GetMotionEmu()->EndTilt();
420}
421
370void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 422void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
371 // TouchBegin always has exactly one touch point, so take the .first() 423 // TouchBegin always has exactly one touch point, so take the .first()
372 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); 424 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
@@ -415,26 +467,20 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
415 InputCommon::GetKeyboard()->ReleaseAllKeys(); 467 InputCommon::GetKeyboard()->ReleaseAllKeys();
416} 468}
417 469
418void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { 470void GRenderWindow::resizeEvent(QResizeEvent* event) {
419 NotifyClientAreaSizeChanged(std::make_pair(width, height)); 471 QWidget::resizeEvent(event);
472 OnFramebufferSizeChanged();
420} 473}
421 474
422std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { 475std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
423 return std::make_unique<GGLContext>(context.get()); 476 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
477 return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
478 }
479 return {};
424} 480}
425 481
426bool GRenderWindow::InitRenderTarget() { 482bool GRenderWindow::InitRenderTarget() {
427 shared_context.reset(); 483 ReleaseRenderTarget();
428 context.reset();
429 if (child) {
430 delete child;
431 }
432 if (container) {
433 delete container;
434 }
435 if (layout()) {
436 delete layout();
437 }
438 484
439 first_frame = false; 485 first_frame = false;
440 486
@@ -451,13 +497,6 @@ bool GRenderWindow::InitRenderTarget() {
451 break; 497 break;
452 } 498 }
453 499
454 container = QWidget::createWindowContainer(child, this);
455 QBoxLayout* layout = new QHBoxLayout(this);
456
457 layout->addWidget(container);
458 layout->setMargin(0);
459 setLayout(layout);
460
461 // Reset minimum required size to avoid resizing issues on the main window after restarting. 500 // Reset minimum required size to avoid resizing issues on the main window after restarting.
462 setMinimumSize(1, 1); 501 setMinimumSize(1, 1);
463 502
@@ -467,14 +506,9 @@ bool GRenderWindow::InitRenderTarget() {
467 hide(); 506 hide();
468 507
469 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); 508 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
470 child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
471 container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
472 509
473 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 510 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
474
475 OnFramebufferSizeChanged(); 511 OnFramebufferSizeChanged();
476 NotifyClientAreaSizeChanged(child->GetSize());
477
478 BackupGeometry(); 512 BackupGeometry();
479 513
480 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 514 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
@@ -486,6 +520,14 @@ bool GRenderWindow::InitRenderTarget() {
486 return true; 520 return true;
487} 521}
488 522
523void GRenderWindow::ReleaseRenderTarget() {
524 if (child_widget) {
525 layout()->removeWidget(child_widget);
526 delete child_widget;
527 child_widget = nullptr;
528 }
529}
530
489void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { 531void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
490 auto& renderer = Core::System::GetInstance().Renderer(); 532 auto& renderer = Core::System::GetInstance().Renderer();
491 533
@@ -521,16 +563,19 @@ bool GRenderWindow::InitializeOpenGL() {
521 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); 563 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
522 // TODO: expose a setting for buffer value (ie default/single/double/triple) 564 // TODO: expose a setting for buffer value (ie default/single/double/triple)
523 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); 565 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
524 shared_context = std::make_unique<QOpenGLContext>(); 566 fmt.setSwapInterval(0);
525 shared_context->setFormat(fmt); 567 QSurfaceFormat::setDefaultFormat(fmt);
526 shared_context->create(); 568
527 context = std::make_unique<QOpenGLContext>(); 569 GMainWindow* parent = GetMainWindow();
528 context->setShareContext(shared_context.get()); 570 QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
529 context->setFormat(fmt); 571 child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
530 context->create(); 572 child_window->create();
531 fmt.setSwapInterval(false); 573 child_widget = createWindowContainer(child_window, this);
532 574 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
533 child = new GGLWidgetInternal(this, shared_context.get()); 575 layout()->addWidget(child_widget);
576
577 core_context = CreateSharedContext();
578
534 return true; 579 return true;
535} 580}
536 581
@@ -559,7 +604,14 @@ bool GRenderWindow::InitializeVulkan() {
559 return false; 604 return false;
560 } 605 }
561 606
562 child = new GVKWidgetInternal(this, vk_instance.get()); 607 GMainWindow* parent = GetMainWindow();
608 QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
609 child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
610 child_window->create();
611 child_widget = createWindowContainer(child_window, this);
612 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
613 layout()->addWidget(child_widget);
614
563 return true; 615 return true;
564#else 616#else
565 QMessageBox::critical(this, tr("Vulkan not available!"), 617 QMessageBox::critical(this, tr("Vulkan not available!"),
@@ -569,7 +621,7 @@ bool GRenderWindow::InitializeVulkan() {
569} 621}
570 622
571bool GRenderWindow::LoadOpenGL() { 623bool GRenderWindow::LoadOpenGL() {
572 Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; 624 Core::Frontend::ScopeAcquireContext acquire_context{*this};
573 if (!gladLoadGL()) { 625 if (!gladLoadGL()) {
574 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), 626 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
575 tr("Your GPU may not support OpenGL 4.3, or you do not have the " 627 tr("Your GPU may not support OpenGL 4.3, or you do not have the "
@@ -621,12 +673,10 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
621 673
622void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 674void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
623 this->emu_thread = emu_thread; 675 this->emu_thread = emu_thread;
624 child->DisablePainting();
625} 676}
626 677
627void GRenderWindow::OnEmulationStopping() { 678void GRenderWindow::OnEmulationStopping() {
628 emu_thread = nullptr; 679 emu_thread = nullptr;
629 child->EnablePainting();
630} 680}
631 681
632void GRenderWindow::showEvent(QShowEvent* event) { 682void GRenderWindow::showEvent(QShowEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 71a2fa321..79b030304 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -11,11 +11,13 @@
11#include <QImage> 11#include <QImage>
12#include <QThread> 12#include <QThread>
13#include <QWidget> 13#include <QWidget>
14#include <QWindow>
14 15
15#include "common/thread.h" 16#include "common/thread.h"
16#include "core/core.h" 17#include "core/core.h"
17#include "core/frontend/emu_window.h" 18#include "core/frontend/emu_window.h"
18 19
20class GRenderWindow;
19class QKeyEvent; 21class QKeyEvent;
20class QScreen; 22class QScreen;
21class QTouchEvent; 23class QTouchEvent;
@@ -26,14 +28,6 @@ class QOpenGLContext;
26class QVulkanInstance; 28class QVulkanInstance;
27#endif 29#endif
28 30
29class GWidgetInternal;
30class GGLWidgetInternal;
31class GVKWidgetInternal;
32class GMainWindow;
33class GRenderWindow;
34class QSurface;
35class QOpenGLContext;
36
37namespace VideoCore { 31namespace VideoCore {
38enum class LoadCallbackStage; 32enum class LoadCallbackStage;
39} 33}
@@ -42,7 +36,7 @@ class EmuThread final : public QThread {
42 Q_OBJECT 36 Q_OBJECT
43 37
44public: 38public:
45 explicit EmuThread(GRenderWindow* render_window); 39 explicit EmuThread(GRenderWindow& window);
46 ~EmuThread() override; 40 ~EmuThread() override;
47 41
48 /** 42 /**
@@ -96,7 +90,11 @@ private:
96 std::mutex running_mutex; 90 std::mutex running_mutex;
97 std::condition_variable running_cv; 91 std::condition_variable running_cv;
98 92
99 GRenderWindow* render_window; 93 /// Only used in asynchronous GPU mode
94 std::unique_ptr<Core::Frontend::GraphicsContext> shared_context;
95
96 /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode
97 Core::Frontend::GraphicsContext& context;
100 98
101signals: 99signals:
102 /** 100 /**
@@ -126,11 +124,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
126 Q_OBJECT 124 Q_OBJECT
127 125
128public: 126public:
129 GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); 127 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
130 ~GRenderWindow() override; 128 ~GRenderWindow() override;
131 129
132 // EmuWindow implementation 130 // EmuWindow implementation.
133 void SwapBuffers() override;
134 void MakeCurrent() override; 131 void MakeCurrent() override;
135 void DoneCurrent() override; 132 void DoneCurrent() override;
136 void PollEvents() override; 133 void PollEvents() override;
@@ -139,30 +136,36 @@ public:
139 void* surface) const override; 136 void* surface) const override;
140 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 137 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
141 138
142 void ForwardKeyPressEvent(QKeyEvent* event);
143 void ForwardKeyReleaseEvent(QKeyEvent* event);
144
145 void BackupGeometry(); 139 void BackupGeometry();
146 void RestoreGeometry(); 140 void RestoreGeometry();
147 void restoreGeometry(const QByteArray& geometry); // overridden 141 void restoreGeometry(const QByteArray& geometry); // overridden
148 QByteArray saveGeometry(); // overridden 142 QByteArray saveGeometry(); // overridden
149 143
150 qreal GetWindowPixelRatio() const; 144 qreal windowPixelRatio() const;
151 std::pair<u32, u32> ScaleTouch(QPointF pos) const;
152 145
153 void closeEvent(QCloseEvent* event) override; 146 void closeEvent(QCloseEvent* event) override;
147
148 void resizeEvent(QResizeEvent* event) override;
149
150 void keyPressEvent(QKeyEvent* event) override;
151 void keyReleaseEvent(QKeyEvent* event) override;
152
153 void mousePressEvent(QMouseEvent* event) override;
154 void mouseMoveEvent(QMouseEvent* event) override;
155 void mouseReleaseEvent(QMouseEvent* event) override;
156
154 bool event(QEvent* event) override; 157 bool event(QEvent* event) override;
155 void focusOutEvent(QFocusEvent* event) override;
156 158
157 void OnClientAreaResized(u32 width, u32 height); 159 void focusOutEvent(QFocusEvent* event) override;
158 160
159 bool InitRenderTarget(); 161 bool InitRenderTarget();
160 162
163 /// Destroy the previous run's child_widget which should also destroy the child_window
164 void ReleaseRenderTarget();
165
161 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); 166 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
162 167
163public slots: 168public slots:
164 void moveContext(); // overridden
165
166 void OnEmulationStarting(EmuThread* emu_thread); 169 void OnEmulationStarting(EmuThread* emu_thread);
167 void OnEmulationStopping(); 170 void OnEmulationStopping();
168 void OnFramebufferSizeChanged(); 171 void OnFramebufferSizeChanged();
@@ -173,6 +176,7 @@ signals:
173 void FirstFrameDisplayed(); 176 void FirstFrameDisplayed();
174 177
175private: 178private:
179 std::pair<u32, u32> ScaleTouch(QPointF pos) const;
176 void TouchBeginEvent(const QTouchEvent* event); 180 void TouchBeginEvent(const QTouchEvent* event);
177 void TouchUpdateEvent(const QTouchEvent* event); 181 void TouchUpdateEvent(const QTouchEvent* event);
178 void TouchEndEvent(); 182 void TouchEndEvent();
@@ -184,15 +188,9 @@ private:
184 bool LoadOpenGL(); 188 bool LoadOpenGL();
185 QStringList GetUnsupportedGLExtensions() const; 189 QStringList GetUnsupportedGLExtensions() const;
186 190
187 QWidget* container = nullptr;
188 GWidgetInternal* child = nullptr;
189
190 EmuThread* emu_thread; 191 EmuThread* emu_thread;
191 // Context that backs the GGLWidgetInternal (and will be used by core to render) 192
192 std::unique_ptr<QOpenGLContext> context; 193 std::unique_ptr<GraphicsContext> core_context;
193 // Context that will be shared between all newly created contexts. This should never be made
194 // current
195 std::unique_ptr<QOpenGLContext> shared_context;
196 194
197#ifdef HAS_VULKAN 195#ifdef HAS_VULKAN
198 std::unique_ptr<QVulkanInstance> vk_instance; 196 std::unique_ptr<QVulkanInstance> vk_instance;
@@ -202,6 +200,15 @@ private:
202 QImage screenshot_image; 200 QImage screenshot_image;
203 201
204 QByteArray geometry; 202 QByteArray geometry;
203
204 /// Native window handle that backs this presentation widget
205 QWindow* child_window = nullptr;
206
207 /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
208 /// put the child_window into a widget then add it to the layout. This child_widget can be
209 /// parented to GRenderWindow and use Qt's lifetime system
210 QWidget* child_widget = nullptr;
211
205 bool first_frame = false; 212 bool first_frame = false;
206 213
207protected: 214protected:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 6209fff75..d0f574147 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -640,6 +640,7 @@ void Config::ReadRendererValues() {
640 ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); 640 ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool();
641 Settings::values.use_asynchronous_gpu_emulation = 641 Settings::values.use_asynchronous_gpu_emulation =
642 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); 642 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
643 Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
643 Settings::values.force_30fps_mode = 644 Settings::values.force_30fps_mode =
644 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); 645 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
645 646
@@ -1074,6 +1075,7 @@ void Config::SaveRendererValues() {
1074 Settings::values.use_accurate_gpu_emulation, false); 1075 Settings::values.use_accurate_gpu_emulation, false);
1075 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), 1076 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
1076 Settings::values.use_asynchronous_gpu_emulation, false); 1077 Settings::values.use_asynchronous_gpu_emulation, false);
1078 WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1077 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); 1079 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
1078 1080
1079 // Cast to double because Qt's written float values are not human-readable 1081 // Cast to double because Qt's written float values are not human-readable
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index ea899c080..fe64c7d81 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -103,6 +103,8 @@ void ConfigureGraphics::SetConfiguration() {
103 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 103 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
104 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 104 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
105 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 105 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
106 ui->use_vsync->setEnabled(runtime_lock);
107 ui->use_vsync->setChecked(Settings::values.use_vsync);
106 ui->force_30fps_mode->setEnabled(runtime_lock); 108 ui->force_30fps_mode->setEnabled(runtime_lock);
107 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); 109 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
108 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 110 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
@@ -120,6 +122,7 @@ void ConfigureGraphics::ApplyConfiguration() {
120 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 122 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
121 Settings::values.use_asynchronous_gpu_emulation = 123 Settings::values.use_asynchronous_gpu_emulation =
122 ui->use_asynchronous_gpu_emulation->isChecked(); 124 ui->use_asynchronous_gpu_emulation->isChecked();
125 Settings::values.use_vsync = ui->use_vsync->isChecked();
123 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); 126 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
124 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 127 Settings::values.bg_red = static_cast<float>(bg_color.redF());
125 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 128 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index db60426ab..9acc7dd93 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -85,6 +85,16 @@
85 </widget> 85 </widget>
86 </item> 86 </item>
87 <item> 87 <item>
88 <widget class="QCheckBox" name="use_vsync">
89 <property name="toolTip">
90 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
91 </property>
92 <property name="text">
93 <string>Use VSync (OpenGL only)</string>
94 </property>
95 </widget>
96 </item>
97 <item>
88 <widget class="QCheckBox" name="use_accurate_gpu_emulation"> 98 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
89 <property name="text"> 99 <property name="text">
90 <string>Use accurate GPU emulation (slow)</string> 100 <string>Use accurate GPU emulation (slow)</string>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 54ca2dc1d..47615adfe 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -20,7 +20,6 @@
20#include "core/file_sys/vfs.h" 20#include "core/file_sys/vfs.h"
21#include "core/file_sys/vfs_real.h" 21#include "core/file_sys/vfs_real.h"
22#include "core/frontend/applets/general_frontend.h" 22#include "core/frontend/applets/general_frontend.h"
23#include "core/frontend/scope_acquire_window_context.h"
24#include "core/hle/service/acc/profile_manager.h" 23#include "core/hle/service/acc/profile_manager.h"
25#include "core/hle/service/am/applet_ae.h" 24#include "core/hle/service/am/applet_ae.h"
26#include "core/hle/service/am/applet_oe.h" 25#include "core/hle/service/am/applet_oe.h"
@@ -985,11 +984,8 @@ void GMainWindow::BootGame(const QString& filename) {
985 return; 984 return;
986 985
987 // Create and start the emulation thread 986 // Create and start the emulation thread
988 emu_thread = std::make_unique<EmuThread>(render_window); 987 emu_thread = std::make_unique<EmuThread>(*render_window);
989 emit EmulationStarting(emu_thread.get()); 988 emit EmulationStarting(emu_thread.get());
990 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
991 render_window->moveContext();
992 }
993 emu_thread->start(); 989 emu_thread->start();
994 990
995 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 991 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
@@ -1087,6 +1083,9 @@ void GMainWindow::ShutdownGame() {
1087 emulation_running = false; 1083 emulation_running = false;
1088 1084
1089 game_path.clear(); 1085 game_path.clear();
1086
1087 // When closing the game, destroy the GLWindow to clear the context after the game is closed
1088 render_window->ReleaseRenderTarget();
1090} 1089}
1091 1090
1092void GMainWindow::StoreRecentFile(const QString& filename) { 1091void GMainWindow::StoreRecentFile(const QString& filename) {
@@ -2215,48 +2214,47 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
2215 QWidget::closeEvent(event); 2214 QWidget::closeEvent(event);
2216} 2215}
2217 2216
2218void GMainWindow::keyPressEvent(QKeyEvent* event) { 2217static bool IsSingleFileDropEvent(const QMimeData* mime) {
2219 if (render_window) { 2218 return mime->hasUrls() && mime->urls().length() == 1;
2220 render_window->ForwardKeyPressEvent(event);
2221 }
2222} 2219}
2223 2220
2224void GMainWindow::keyReleaseEvent(QKeyEvent* event) { 2221void GMainWindow::AcceptDropEvent(QDropEvent* event) {
2225 if (render_window) { 2222 if (IsSingleFileDropEvent(event->mimeData())) {
2226 render_window->ForwardKeyReleaseEvent(event); 2223 event->setDropAction(Qt::DropAction::LinkAction);
2224 event->accept();
2227 } 2225 }
2228} 2226}
2229 2227
2230static bool IsSingleFileDropEvent(QDropEvent* event) { 2228bool GMainWindow::DropAction(QDropEvent* event) {
2231 const QMimeData* mimeData = event->mimeData(); 2229 if (!IsSingleFileDropEvent(event->mimeData())) {
2232 return mimeData->hasUrls() && mimeData->urls().length() == 1; 2230 return false;
2233}
2234
2235void GMainWindow::dropEvent(QDropEvent* event) {
2236 if (!IsSingleFileDropEvent(event)) {
2237 return;
2238 } 2231 }
2239 2232
2240 const QMimeData* mime_data = event->mimeData(); 2233 const QMimeData* mime_data = event->mimeData();
2241 const QString filename = mime_data->urls().at(0).toLocalFile(); 2234 const QString& filename = mime_data->urls().at(0).toLocalFile();
2242 2235
2243 if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { 2236 if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) {
2237 // Amiibo
2244 LoadAmiibo(filename); 2238 LoadAmiibo(filename);
2245 } else { 2239 } else {
2240 // Game
2246 if (ConfirmChangeGame()) { 2241 if (ConfirmChangeGame()) {
2247 BootGame(filename); 2242 BootGame(filename);
2248 } 2243 }
2249 } 2244 }
2245 return true;
2246}
2247
2248void GMainWindow::dropEvent(QDropEvent* event) {
2249 DropAction(event);
2250} 2250}
2251 2251
2252void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { 2252void GMainWindow::dragEnterEvent(QDragEnterEvent* event) {
2253 if (IsSingleFileDropEvent(event)) { 2253 AcceptDropEvent(event);
2254 event->acceptProposedAction();
2255 }
2256} 2254}
2257 2255
2258void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { 2256void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
2259 event->acceptProposedAction(); 2257 AcceptDropEvent(event);
2260} 2258}
2261 2259
2262bool GMainWindow::ConfirmChangeGame() { 2260bool GMainWindow::ConfirmChangeGame() {
@@ -2377,6 +2375,7 @@ int main(int argc, char* argv[]) {
2377 2375
2378 // Enables the core to make the qt created contexts current on std::threads 2376 // Enables the core to make the qt created contexts current on std::threads
2379 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 2377 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
2378 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
2380 QApplication app(argc, argv); 2379 QApplication app(argc, argv);
2381 2380
2382 // Qt changes the locale and causes issues in float conversion using std::to_string() when 2381 // Qt changes the locale and causes issues in float conversion using std::to_string() when
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 8eba2172c..a67125567 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -78,6 +78,9 @@ public:
78 78
79 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; 79 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
80 80
81 bool DropAction(QDropEvent* event);
82 void AcceptDropEvent(QDropEvent* event);
83
81signals: 84signals:
82 85
83 /** 86 /**
@@ -264,8 +267,4 @@ protected:
264 void dropEvent(QDropEvent* event) override; 267 void dropEvent(QDropEvent* event) override;
265 void dragEnterEvent(QDragEnterEvent* event) override; 268 void dragEnterEvent(QDragEnterEvent* event) override;
266 void dragMoveEvent(QDragMoveEvent* event) override; 269 void dragMoveEvent(QDragMoveEvent* event) override;
267
268 // Overrides used to forward signals to the render window when the focus moves out.
269 void keyPressEvent(QKeyEvent* event) override;
270 void keyReleaseEvent(QKeyEvent* event) override;
271}; 270};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 96f1ce3af..b77c12baf 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -390,6 +390,8 @@ void Config::ReadValues() {
390 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); 390 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
391 Settings::values.use_asynchronous_gpu_emulation = 391 Settings::values.use_asynchronous_gpu_emulation =
392 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 392 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
393 Settings::values.use_vsync =
394 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1));
393 395
394 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 396 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0));
395 Settings::values.bg_green = 397 Settings::values.bg_green =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 8a2b658cd..df7473858 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -150,6 +150,11 @@ use_accurate_gpu_emulation =
150# 0 : Off (slow), 1 (default): On (fast) 150# 0 : Off (slow), 1 (default): On (fast)
151use_asynchronous_gpu_emulation = 151use_asynchronous_gpu_emulation =
152 152
153# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
154# so only turn this off if you notice a speed difference.
155# 0: Off, 1 (default): On
156use_vsync =
157
153# The clear color for the renderer. What shows up on the sides of the bottom screen. 158# The clear color for the renderer. What shows up on the sides of the bottom screen.
154# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 159# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
155bg_red = 160bg_red =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e96139885..19584360c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,7 +13,7 @@
13#include "input_common/sdl/sdl.h" 13#include "input_common/sdl/sdl.h"
14#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
15 15
16EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { 16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} {
17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
19 exit(1); 19 exit(1);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index b38f56661..fffac4252 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -10,9 +10,13 @@
10 10
11struct SDL_Window; 11struct SDL_Window;
12 12
13namespace Core {
14class System;
15}
16
13class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 17class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
14public: 18public:
15 explicit EmuWindow_SDL2(bool fullscreen); 19 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen);
16 ~EmuWindow_SDL2(); 20 ~EmuWindow_SDL2();
17 21
18 /// Polls window events 22 /// Polls window events
@@ -24,6 +28,9 @@ public:
24 /// Returns if window is shown (not minimized) 28 /// Returns if window is shown (not minimized)
25 bool IsShown() const override; 29 bool IsShown() const override;
26 30
31 /// Presents the next frame
32 virtual void Present() = 0;
33
27protected: 34protected:
28 /// Called by PollEvents when a key is pressed or released. 35 /// Called by PollEvents when a key is pressed or released.
29 void OnKeyEvent(int key, u8 state); 36 void OnKeyEvent(int key, u8 state);
@@ -55,6 +62,9 @@ protected:
55 /// Called when a configuration change affects the minimal size of the window 62 /// Called when a configuration change affects the minimal size of the window
56 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; 63 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
57 64
65 /// Instance of the system, used to access renderer for the presentation thread
66 Core::System& system;
67
58 /// Is the window still open? 68 /// Is the window still open?
59 bool is_open = true; 69 bool is_open = true;
60 70
@@ -62,7 +72,7 @@ protected:
62 bool is_shown = true; 72 bool is_shown = true;
63 73
64 /// Internal SDL2 render window 74 /// Internal SDL2 render window
65 SDL_Window* render_window; 75 SDL_Window* render_window{};
66 76
67 /// Keeps track of how often to update the title bar during gameplay 77 /// Keeps track of how often to update the title bar during gameplay
68 u32 last_time = 0; 78 u32 last_time = 0;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 7ffa0ac09..c0d373477 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -13,24 +13,25 @@
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/scm_rev.h" 14#include "common/scm_rev.h"
15#include "common/string_util.h" 15#include "common/string_util.h"
16#include "core/core.h"
16#include "core/settings.h" 17#include "core/settings.h"
17#include "input_common/keyboard.h" 18#include "input_common/keyboard.h"
18#include "input_common/main.h" 19#include "input_common/main.h"
19#include "input_common/motion_emu.h" 20#include "input_common/motion_emu.h"
21#include "video_core/renderer_base.h"
20#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 22#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
21 23
22class SDLGLContext : public Core::Frontend::GraphicsContext { 24class SDLGLContext : public Core::Frontend::GraphicsContext {
23public: 25public:
24 explicit SDLGLContext() { 26 explicit SDLGLContext() {
25 // create a hidden window to make the shared context against 27 // create a hidden window to make the shared context against
26 window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position 28 window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
27 SDL_WINDOWPOS_UNDEFINED, // y position 29 SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
28 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
29 SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
30 context = SDL_GL_CreateContext(window); 30 context = SDL_GL_CreateContext(window);
31 } 31 }
32 32
33 ~SDLGLContext() { 33 ~SDLGLContext() {
34 DoneCurrent();
34 SDL_GL_DeleteContext(context); 35 SDL_GL_DeleteContext(context);
35 SDL_DestroyWindow(window); 36 SDL_DestroyWindow(window);
36 } 37 }
@@ -43,8 +44,6 @@ public:
43 SDL_GL_MakeCurrent(window, nullptr); 44 SDL_GL_MakeCurrent(window, nullptr);
44 } 45 }
45 46
46 void SwapBuffers() override {}
47
48private: 47private:
49 SDL_Window* window; 48 SDL_Window* window;
50 SDL_GLContext context; 49 SDL_GLContext context;
@@ -80,7 +79,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
80 return unsupported_ext.empty(); 79 return unsupported_ext.empty();
81} 80}
82 81
83EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { 82EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen)
83 : EmuWindow_SDL2{system, fullscreen} {
84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
85 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 85 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
86 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 86 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -90,6 +90,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree
90 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 90 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
91 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 91 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
92 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); 92 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
93 SDL_GL_SetSwapInterval(0);
93 94
94 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, 95 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
95 Common::g_scm_branch, Common::g_scm_desc); 96 Common::g_scm_branch, Common::g_scm_desc);
@@ -105,13 +106,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree
105 exit(1); 106 exit(1);
106 } 107 }
107 108
109 dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
110 SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
111
108 if (fullscreen) { 112 if (fullscreen) {
109 Fullscreen(); 113 Fullscreen();
110 } 114 }
111 gl_context = SDL_GL_CreateContext(render_window);
112 115
113 if (gl_context == nullptr) { 116 window_context = SDL_GL_CreateContext(render_window);
114 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); 117 core_context = CreateSharedContext();
118
119 if (window_context == nullptr) {
120 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError());
121 exit(1);
122 }
123 if (core_context == nullptr) {
124 LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError());
115 exit(1); 125 exit(1);
116 } 126 }
117 127
@@ -128,28 +138,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree
128 OnResize(); 138 OnResize();
129 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 139 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
130 SDL_PumpEvents(); 140 SDL_PumpEvents();
131 SDL_GL_SetSwapInterval(false);
132 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, 141 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
133 Common::g_scm_desc); 142 Common::g_scm_desc);
134 Settings::LogSettings(); 143 Settings::LogSettings();
135
136 DoneCurrent();
137} 144}
138 145
139EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { 146EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
140 SDL_GL_DeleteContext(gl_context); 147 core_context.reset();
141} 148 SDL_GL_DeleteContext(window_context);
142
143void EmuWindow_SDL2_GL::SwapBuffers() {
144 SDL_GL_SwapWindow(render_window);
145} 149}
146 150
147void EmuWindow_SDL2_GL::MakeCurrent() { 151void EmuWindow_SDL2_GL::MakeCurrent() {
148 SDL_GL_MakeCurrent(render_window, gl_context); 152 core_context->MakeCurrent();
149} 153}
150 154
151void EmuWindow_SDL2_GL::DoneCurrent() { 155void EmuWindow_SDL2_GL::DoneCurrent() {
152 SDL_GL_MakeCurrent(render_window, nullptr); 156 core_context->DoneCurrent();
153} 157}
154 158
155void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 159void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
@@ -161,3 +165,13 @@ void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, voi
161std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { 165std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
162 return std::make_unique<SDLGLContext>(); 166 return std::make_unique<SDLGLContext>();
163} 167}
168
169void EmuWindow_SDL2_GL::Present() {
170 SDL_GL_MakeCurrent(render_window, window_context);
171 SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0);
172 while (IsOpen()) {
173 system.Renderer().TryPresent(100);
174 SDL_GL_SwapWindow(render_window);
175 }
176 SDL_GL_MakeCurrent(render_window, nullptr);
177}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index c753085a8..b80669ff0 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -10,17 +10,12 @@
10 10
11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
12public: 12public:
13 explicit EmuWindow_SDL2_GL(bool fullscreen); 13 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
14 ~EmuWindow_SDL2_GL(); 14 ~EmuWindow_SDL2_GL();
15 15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Makes the graphics context current for the caller thread
20 void MakeCurrent() override; 16 void MakeCurrent() override;
21
22 /// Releases the GL context from the caller thread
23 void DoneCurrent() override; 17 void DoneCurrent() override;
18 void Present() override;
24 19
25 /// Ignored in OpenGL 20 /// Ignored in OpenGL
26 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 21 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
@@ -29,10 +24,17 @@ public:
29 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 24 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
30 25
31private: 26private:
27 /// Fake hidden window for the core context
28 SDL_Window* dummy_window{};
29
32 /// Whether the GPU and driver supports the OpenGL extension required 30 /// Whether the GPU and driver supports the OpenGL extension required
33 bool SupportsRequiredGLExtensions(); 31 bool SupportsRequiredGLExtensions();
34 32
35 using SDL_GLContext = void*; 33 using SDL_GLContext = void*;
34
36 /// The OpenGL context associated with the window 35 /// The OpenGL context associated with the window
37 SDL_GLContext gl_context; 36 SDL_GLContext window_context;
37
38 /// The OpenGL context associated with the core
39 std::unique_ptr<Core::Frontend::GraphicsContext> core_context;
38}; 40};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index a203f0da9..abcc58165 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -15,7 +15,8 @@
15#include "core/settings.h" 15#include "core/settings.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
17 17
18EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { 18EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
19 : EmuWindow_SDL2{system, fullscreen} {
19 if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { 20 if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
20 LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); 21 LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
21 exit(EXIT_FAILURE); 22 exit(EXIT_FAILURE);
@@ -110,8 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
110 vkDestroyInstance(vk_instance, nullptr); 111 vkDestroyInstance(vk_instance, nullptr);
111} 112}
112 113
113void EmuWindow_SDL2_VK::SwapBuffers() {}
114
115void EmuWindow_SDL2_VK::MakeCurrent() { 114void EmuWindow_SDL2_VK::MakeCurrent() {
116 // Unused on Vulkan 115 // Unused on Vulkan
117} 116}
@@ -160,3 +159,7 @@ bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanc
160 return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); 159 return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
161 }) != layers.end(); 160 }) != layers.end();
162} 161}
162
163void EmuWindow_SDL2_VK::Present() {
164 // TODO (bunnei): ImplementMe
165}
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 2a7c06a24..1eb8c0868 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -10,19 +10,12 @@
10 10
11class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 11class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
12public: 12public:
13 explicit EmuWindow_SDL2_VK(bool fullscreen); 13 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
14 ~EmuWindow_SDL2_VK(); 14 ~EmuWindow_SDL2_VK();
15 15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Makes the graphics context current for the caller thread
20 void MakeCurrent() override; 16 void MakeCurrent() override;
21
22 /// Releases the GL context from the caller thread
23 void DoneCurrent() override; 17 void DoneCurrent() override;
24 18 void Present() override;
25 /// Retrieves Vulkan specific handlers from the window
26 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 19 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
27 void* surface) const override; 20 void* surface) const override;
28 21
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 325795321..babf4c3a4 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -177,14 +177,16 @@ int main(int argc, char** argv) {
177 Settings::values.use_gdbstub = use_gdbstub; 177 Settings::values.use_gdbstub = use_gdbstub;
178 Settings::Apply(); 178 Settings::Apply();
179 179
180 Core::System& system{Core::System::GetInstance()};
181
180 std::unique_ptr<EmuWindow_SDL2> emu_window; 182 std::unique_ptr<EmuWindow_SDL2> emu_window;
181 switch (Settings::values.renderer_backend) { 183 switch (Settings::values.renderer_backend) {
182 case Settings::RendererBackend::OpenGL: 184 case Settings::RendererBackend::OpenGL:
183 emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); 185 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen);
184 break; 186 break;
185 case Settings::RendererBackend::Vulkan: 187 case Settings::RendererBackend::Vulkan:
186#ifdef HAS_VULKAN 188#ifdef HAS_VULKAN
187 emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); 189 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen);
188 break; 190 break;
189#else 191#else
190 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 192 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -192,12 +194,6 @@ int main(int argc, char** argv) {
192#endif 194#endif
193 } 195 }
194 196
195 if (!Settings::values.use_multi_core) {
196 // Single core mode must acquire OpenGL context for entire emulation session
197 emu_window->MakeCurrent();
198 }
199
200 Core::System& system{Core::System::GetInstance()};
201 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 197 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
202 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 198 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
203 system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); 199 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
@@ -234,12 +230,23 @@ int main(int argc, char** argv) {
234 230
235 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); 231 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
236 232
237 emu_window->MakeCurrent();
238 system.Renderer().Rasterizer().LoadDiskResources(); 233 system.Renderer().Rasterizer().LoadDiskResources();
239 234
235 // Acquire render context for duration of the thread if this is the rendering thread
236 if (!Settings::values.use_asynchronous_gpu_emulation) {
237 emu_window->MakeCurrent();
238 }
239 SCOPE_EXIT({
240 if (!Settings::values.use_asynchronous_gpu_emulation) {
241 emu_window->DoneCurrent();
242 }
243 });
244
245 std::thread render_thread([&emu_window] { emu_window->Present(); });
240 while (emu_window->IsOpen()) { 246 while (emu_window->IsOpen()) {
241 system.RunLoop(); 247 system.RunLoop();
242 } 248 }
249 render_thread.join();
243 250
244 system.Shutdown(); 251 system.Shutdown();
245 252
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index f2cc4a797..a1bdb1a12 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -112,10 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
112 SDL_Quit(); 112 SDL_Quit();
113} 113}
114 114
115void EmuWindow_SDL2_Hide::SwapBuffers() {
116 SDL_GL_SwapWindow(render_window);
117}
118
119void EmuWindow_SDL2_Hide::PollEvents() {} 115void EmuWindow_SDL2_Hide::PollEvents() {}
120 116
121void EmuWindow_SDL2_Hide::MakeCurrent() { 117void EmuWindow_SDL2_Hide::MakeCurrent() {
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index c7fccc002..b13e15309 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -13,9 +13,6 @@ public:
13 explicit EmuWindow_SDL2_Hide(); 13 explicit EmuWindow_SDL2_Hide();
14 ~EmuWindow_SDL2_Hide(); 14 ~EmuWindow_SDL2_Hide();
15 15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Polls window events 16 /// Polls window events
20 void PollEvents() override; 17 void PollEvents() override;
21 18