summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp6
-rw-r--r--src/core/file_sys/content_archive.cpp10
-rw-r--r--src/core/file_sys/ips_layer.cpp6
-rw-r--r--src/core/file_sys/submission_package.cpp10
-rw-r--r--src/core/file_sys/vfs.cpp7
-rw-r--r--src/core/file_sys/vfs_offset.cpp7
-rw-r--r--src/core/file_sys/vfs_static.h8
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp8
-rw-r--r--src/core/hle/service/hid/controllers/npad.h10
-rw-r--r--src/core/hle/service/hid/hid.cpp17
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp4
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp6
-rw-r--r--src/core/hle/service/sockets/bsd.cpp2
-rw-r--r--src/core/loader/nso.cpp6
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/macro/macro.cpp2
-rw-r--r--src/video_core/memory_manager.cpp6
-rw-r--r--src/video_core/renderer_base.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp12
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp288
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h16
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp5
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h1
-rw-r--r--src/video_core/shader/ast.h25
-rw-r--r--src/video_core/shader/control_flow.cpp27
-rw-r--r--src/video_core/shader/track.cpp4
-rw-r--r--src/video_core/texture_cache/surface_base.cpp10
-rw-r--r--src/yuzu/bootmanager.cpp23
-rw-r--r--src/yuzu/game_list.cpp80
-rw-r--r--src/yuzu/game_list.h24
-rw-r--r--src/yuzu/install_dialog.h5
-rw-r--r--src/yuzu/main.cpp15
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp15
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h5
-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.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
41 files changed, 217 insertions, 501 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 54556e0f9..caefc09f4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne
34 CoprocReg CRm, unsigned opc2) { 34 CoprocReg CRm, unsigned opc2) {
35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, 35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
36 CRm, opc2); 36 CRm, opc2);
37 return {}; 37 return std::nullopt;
38} 38}
39 39
40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, 40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
@@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans
115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", 115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
116 long_transfer ? "l" : "", CRd); 116 long_transfer ? "l" : "", CRd);
117 } 117 }
118 return {}; 118 return std::nullopt;
119} 119}
120 120
121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, 121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
@@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran
127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", 127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
128 long_transfer ? "l" : "", CRd); 128 long_transfer ? "l" : "", CRd);
129 } 129 }
130 return {}; 130 return std::nullopt;
131} 131}
132 132
133} // namespace Core 133} // namespace Core
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 426fb6bb5..76af47ff9 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -323,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
323 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); 323 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
324 subsection_buckets.back().entries.push_back({size, {0}, 0}); 324 subsection_buckets.back().entries.push_back({size, {0}, 0});
325 325
326 std::optional<Core::Crypto::Key128> key = {}; 326 std::optional<Core::Crypto::Key128> key;
327 if (encrypted) { 327 if (encrypted) {
328 if (has_rights_id) { 328 if (has_rights_id) {
329 status = Loader::ResultStatus::Success; 329 status = Loader::ResultStatus::Success;
@@ -442,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
442 memcpy(rights_id.data(), header.rights_id.data(), 16); 442 memcpy(rights_id.data(), header.rights_id.data(), 16);
443 if (rights_id == u128{}) { 443 if (rights_id == u128{}) {
444 status = Loader::ResultStatus::ErrorInvalidRightsID; 444 status = Loader::ResultStatus::ErrorInvalidRightsID;
445 return {}; 445 return std::nullopt;
446 } 446 }
447 447
448 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); 448 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
449 if (titlekey == Core::Crypto::Key128{}) { 449 if (titlekey == Core::Crypto::Key128{}) {
450 status = Loader::ResultStatus::ErrorMissingTitlekey; 450 status = Loader::ResultStatus::ErrorMissingTitlekey;
451 return {}; 451 return std::nullopt;
452 } 452 }
453 453
454 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { 454 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
455 status = Loader::ResultStatus::ErrorMissingTitlekek; 455 status = Loader::ResultStatus::ErrorMissingTitlekek;
456 return {}; 456 return std::nullopt;
457 } 457 }
458 458
459 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 459 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -477,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
477 case NCASectionCryptoType::BKTR: 477 case NCASectionCryptoType::BKTR:
478 LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 478 LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
479 { 479 {
480 std::optional<Core::Crypto::Key128> key = {}; 480 std::optional<Core::Crypto::Key128> key;
481 if (has_rights_id) { 481 if (has_rights_id) {
482 status = Loader::ResultStatus::Success; 482 status = Loader::ResultStatus::Success;
483 key = GetTitlekey(); 483 key = GetTitlekey();
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index a08a70efd..dd779310f 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() {
245 245
246 // Read rest of patch 246 // Read rest of patch
247 while (true) { 247 while (true) {
248 if (i + 1 >= lines.size()) 248 if (i + 1 >= lines.size()) {
249 break; 249 break;
250 const auto patch_line = lines[++i]; 250 }
251
252 const auto& patch_line = lines[++i];
251 253
252 // Start of new patch 254 // Start of new patch
253 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { 255 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index b9ce93b7c..aab957bf2 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -267,9 +267,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
267 } 267 }
268 268
269 const CNMT cnmt(inner_file); 269 const CNMT cnmt(inner_file);
270 auto& ncas_title = ncas[cnmt.GetTitleID()];
271 270
272 ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca; 271 ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca;
272
273 for (const auto& rec : cnmt.GetContentRecords()) { 273 for (const auto& rec : cnmt.GetContentRecords()) {
274 const auto id_string = Common::HexToString(rec.nca_id, false); 274 const auto id_string = Common::HexToString(rec.nca_id, false);
275 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); 275 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -287,12 +287,12 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
287 287
288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); 288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
289 if (next_nca->GetType() == NCAContentType::Program) { 289 if (next_nca->GetType() == NCAContentType::Program) {
290 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 290 program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
291 } 291 }
292 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 292 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
293 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 293 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
294 (cnmt.GetTitleID() & 0x800) != 0)) { 294 (next_nca->GetTitleId() & 0x800) != 0)) {
295 ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca); 295 ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
296 } 296 }
297 } 297 }
298 298
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index a4c3f67c4..b2f026b6d 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -169,11 +169,12 @@ VfsDirectory::~VfsDirectory() = default;
169 169
170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { 170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
171 u8 out{}; 171 u8 out{};
172 std::size_t size = Read(&out, 1, offset); 172 const std::size_t size = Read(&out, sizeof(u8), offset);
173 if (size == 1) 173 if (size == 1) {
174 return out; 174 return out;
175 }
175 176
176 return {}; 177 return std::nullopt;
177} 178}
178 179
179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { 180std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index c96f88488..7714d3de5 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
58} 58}
59 59
60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { 60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
61 if (r_offset < size) 61 if (r_offset >= size) {
62 return file->ReadByte(offset + r_offset); 62 return std::nullopt;
63 }
63 64
64 return {}; 65 return file->ReadByte(offset + r_offset);
65} 66}
66 67
67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { 68std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 9f5a90b1b..8b27c30fa 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -54,9 +54,11 @@ public:
54 } 54 }
55 55
56 std::optional<u8> ReadByte(std::size_t offset) const override { 56 std::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size) 57 if (offset >= size) {
58 return value; 58 return std::nullopt;
59 return {}; 59 }
60
61 return value;
60 } 62 }
61 63
62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { 64 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index b65d59373..620386cd1 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -593,6 +593,14 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
593 return hold_type; 593 return hold_type;
594} 594}
595 595
596void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
597 handheld_activation_mode = activation_mode;
598}
599
600Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
601 return handheld_activation_mode;
602}
603
596void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { 604void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
597 const std::size_t npad_index = NPadIdToIndex(npad_id); 605 const std::size_t npad_index = NPadIdToIndex(npad_id);
598 ASSERT(npad_index < shared_memory_entries.size()); 606 ASSERT(npad_index < shared_memory_entries.size());
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 78e7c320b..654d97c3f 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -74,6 +74,12 @@ public:
74 Single = 1, 74 Single = 1,
75 }; 75 };
76 76
77 enum class NpadHandheldActivationMode : u64 {
78 Dual = 0,
79 Single = 1,
80 None = 2,
81 };
82
77 enum class NPadControllerType { 83 enum class NPadControllerType {
78 None, 84 None,
79 ProController, 85 ProController,
@@ -110,6 +116,9 @@ public:
110 void SetHoldType(NpadHoldType joy_hold_type); 116 void SetHoldType(NpadHoldType joy_hold_type);
111 NpadHoldType GetHoldType() const; 117 NpadHoldType GetHoldType() const;
112 118
119 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
120 NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
121
113 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); 122 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
114 123
115 void VibrateController(const std::vector<u32>& controller_ids, 124 void VibrateController(const std::vector<u32>& controller_ids,
@@ -369,6 +378,7 @@ private:
369 MotionArray motions; 378 MotionArray motions;
370 std::vector<u32> supported_npad_id_types{}; 379 std::vector<u32> supported_npad_id_types{};
371 NpadHoldType hold_type{NpadHoldType::Vertical}; 380 NpadHoldType hold_type{NpadHoldType::Vertical};
381 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
372 // Each controller should have their own styleset changed event 382 // Each controller should have their own styleset changed event
373 std::array<Kernel::EventPair, 10> styleset_changed_events; 383 std::array<Kernel::EventPair, 10> styleset_changed_events;
374 Vibration last_processed_vibration{}; 384 Vibration last_processed_vibration{};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 302a25540..395e83b3f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -40,7 +40,7 @@ namespace Service::HID {
40// Updating period for each HID device. 40// Updating period for each HID device.
41// HID is polled every 15ms, this value was derived from 41// HID is polled every 15ms, this value was derived from
42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz) 43constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
45 45
46IAppletResource::IAppletResource(Core::System& system) 46IAppletResource::IAppletResource(Core::System& system)
@@ -739,8 +739,11 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
739 const auto applet_resource_user_id{rp.Pop<u64>()}; 739 const auto applet_resource_user_id{rp.Pop<u64>()};
740 const auto mode{rp.Pop<u64>()}; 740 const auto mode{rp.Pop<u64>()};
741 741
742 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", 742 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id,
743 applet_resource_user_id, mode); 743 mode);
744
745 applet_resource->GetController<Controller_NPad>(HidController::NPad)
746 .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode});
744 747
745 IPC::ResponseBuilder rb{ctx, 2}; 748 IPC::ResponseBuilder rb{ctx, 2};
746 rb.Push(RESULT_SUCCESS); 749 rb.Push(RESULT_SUCCESS);
@@ -750,11 +753,13 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
750 IPC::RequestParser rp{ctx}; 753 IPC::RequestParser rp{ctx};
751 const auto applet_resource_user_id{rp.Pop<u64>()}; 754 const auto applet_resource_user_id{rp.Pop<u64>()};
752 755
753 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 756 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
754 applet_resource_user_id);
755 757
756 IPC::ResponseBuilder rb{ctx, 2}; 758 IPC::ResponseBuilder rb{ctx, 4};
757 rb.Push(RESULT_SUCCESS); 759 rb.Push(RESULT_SUCCESS);
760 rb.Push<u64>(
761 static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
762 .GetNpadHandheldActivationMode()));
758} 763}
759 764
760void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { 765void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index d4ba88147..39bd2a45b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -265,7 +265,7 @@ std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gp
265 } 265 }
266 } 266 }
267 267
268 return {}; 268 return std::nullopt;
269} 269}
270 270
271void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, 271void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
@@ -286,7 +286,7 @@ std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
286 return size; 286 return size;
287 } 287 }
288 288
289 return {}; 289 return std::nullopt;
290} 290}
291 291
292} // namespace Service::Nvidia::Devices 292} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index d7a1bef91..7706a5590 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -54,7 +54,7 @@ struct EventInterface {
54 } 54 }
55 mask = mask >> 1; 55 mask = mask >> 1;
56 } 56 }
57 return {}; 57 return std::nullopt;
58 } 58 }
59 void SetEventStatus(const u32 event_id, EventState new_status) { 59 void SetEventStatus(const u32 event_id, EventState new_status) {
60 EventState old_status = status[event_id]; 60 EventState old_status = status[event_id];
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index f644a460d..c64673dba 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -114,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
114 [&](const VI::Display& display) { return display.GetName() == name; }); 114 [&](const VI::Display& display) { return display.GetName() == name; });
115 115
116 if (itr == displays.end()) { 116 if (itr == displays.end()) {
117 return {}; 117 return std::nullopt;
118 } 118 }
119 119
120 return itr->GetID(); 120 return itr->GetID();
@@ -124,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
124 auto* const display = FindDisplay(display_id); 124 auto* const display = FindDisplay(display_id);
125 125
126 if (display == nullptr) { 126 if (display == nullptr) {
127 return {}; 127 return std::nullopt;
128 } 128 }
129 129
130 const u64 layer_id = next_layer_id++; 130 const u64 layer_id = next_layer_id++;
@@ -144,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
144 const auto* const layer = FindLayer(display_id, layer_id); 144 const auto* const layer = FindLayer(display_id, layer_id);
145 145
146 if (layer == nullptr) { 146 if (layer == nullptr) {
147 return {}; 147 return std::nullopt;
148 } 148 }
149 149
150 return layer->GetBufferQueue().GetId(); 150 return layer->GetBufferQueue().GetId();
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 7b9dd42d8..a74be9370 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -497,7 +497,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u
497 return {0, Errno::SUCCESS}; 497 return {0, Errno::SUCCESS};
498 } 498 }
499 499
500 std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd]; 500 const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
501 if (!descriptor) { 501 if (!descriptor) {
502 LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd); 502 LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
503 pollfd.revents = POLL_NVAL; 503 pollfd.revents = POLL_NVAL;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 60373cc5f..1e70f6e11 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -76,16 +76,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
76 bool should_pass_arguments, bool load_into_process, 76 bool should_pass_arguments, bool load_into_process,
77 std::optional<FileSys::PatchManager> pm) { 77 std::optional<FileSys::PatchManager> pm) {
78 if (file.GetSize() < sizeof(NSOHeader)) { 78 if (file.GetSize() < sizeof(NSOHeader)) {
79 return {}; 79 return std::nullopt;
80 } 80 }
81 81
82 NSOHeader nso_header{}; 82 NSOHeader nso_header{};
83 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { 83 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
84 return {}; 84 return std::nullopt;
85 } 85 }
86 86
87 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { 87 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
88 return {}; 88 return std::nullopt;
89 } 89 }
90 90
91 // Build program image 91 // Build program image
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 86d17c6cb..c3f4829d7 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -567,7 +567,7 @@ struct Memory::Impl {
567 * @param page_table The page table to use to perform the mapping. 567 * @param page_table The page table to use to perform the mapping.
568 * @param base The base address to begin mapping at. 568 * @param base The base address to begin mapping at.
569 * @param size The total size of the range in bytes. 569 * @param size The total size of the range in bytes.
570 * @param memory The memory to map. 570 * @param target The target address to begin mapping from.
571 * @param type The page type to map the memory as. 571 * @param type The page type to map the memory as.
572 */ 572 */
573 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, 573 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 33854445f..57ebc785f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -597,7 +597,7 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
597 // Deferred. 597 // Deferred.
598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed, 598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
599 system.GPU().GetTicks()); 599 system.GPU().GetTicks());
600 return {}; 600 return std::nullopt;
601 default: 601 default:
602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", 602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
603 static_cast<u32>(regs.query.query_get.select.Value())); 603 static_cast<u32>(regs.query.query_get.select.Value()));
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index a50e7b4e0..cd21a2112 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -36,7 +36,7 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
36 } 36 }
37 } else { 37 } else {
38 // Macro not compiled, check if it's uploaded and if so, compile it 38 // Macro not compiled, check if it's uploaded and if so, compile it
39 std::optional<u32> mid_method = std::nullopt; 39 std::optional<u32> mid_method;
40 const auto macro_code = uploaded_macro_code.find(method); 40 const auto macro_code = uploaded_macro_code.find(method);
41 if (macro_code == uploaded_macro_code.end()) { 41 if (macro_code == uploaded_macro_code.end()) {
42 for (const auto& [method_base, code] : uploaded_macro_code) { 42 for (const auto& [method_base, code] : uploaded_macro_code) {
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 16b2aaa27..02cf53d15 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -58,7 +58,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
58std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { 58std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
59 for (u64 offset{}; offset < size; offset += page_size) { 59 for (u64 offset{}; offset < size; offset += page_size) {
60 if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) { 60 if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
61 return {}; 61 return std::nullopt;
62 } 62 }
63 } 63 }
64 64
@@ -135,13 +135,13 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size
135 } 135 }
136 } 136 }
137 137
138 return {}; 138 return std::nullopt;
139} 139}
140 140
141std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { 141std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
142 const auto page_entry{GetPageEntry(gpu_addr)}; 142 const auto page_entry{GetPageEntry(gpu_addr)};
143 if (!page_entry.IsValid()) { 143 if (!page_entry.IsValid()) {
144 return {}; 144 return std::nullopt;
145 } 145 }
146 146
147 return page_entry.ToAddress() + (gpu_addr & page_mask); 147 return page_entry.ToAddress() + (gpu_addr & page_mask);
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 649074acd..5c650808b 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -46,11 +46,6 @@ public:
46 /// Finalize rendering the guest frame and draw into the presentation texture 46 /// Finalize rendering the guest frame and draw into the presentation texture
47 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; 47 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
48 48
49 /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
50 /// specific implementation)
51 /// Returns true if a frame was drawn
52 virtual bool TryPresent(int timeout_ms) = 0;
53
54 // Getter/setter functions: 49 // Getter/setter functions:
55 // ------------------------ 50 // ------------------------
56 51
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index ce3a65122..bbb8fb095 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -813,7 +813,7 @@ private:
813 const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element); 813 const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
814 const auto it = transform_feedback.find(location); 814 const auto it = transform_feedback.find(location);
815 if (it == transform_feedback.end()) { 815 if (it == transform_feedback.end()) {
816 return {}; 816 return std::nullopt;
817 } 817 }
818 return it->second.components; 818 return it->second.components;
819 } 819 }
@@ -1295,21 +1295,21 @@ private:
1295 switch (element) { 1295 switch (element) {
1296 case 0: 1296 case 0:
1297 UNIMPLEMENTED(); 1297 UNIMPLEMENTED();
1298 return {}; 1298 return std::nullopt;
1299 case 1: 1299 case 1:
1300 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) { 1300 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
1301 return {}; 1301 return std::nullopt;
1302 } 1302 }
1303 return {{"gl_Layer", Type::Int}}; 1303 return {{"gl_Layer", Type::Int}};
1304 case 2: 1304 case 2:
1305 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) { 1305 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
1306 return {}; 1306 return std::nullopt;
1307 } 1307 }
1308 return {{"gl_ViewportIndex", Type::Int}}; 1308 return {{"gl_ViewportIndex", Type::Int}};
1309 case 3: 1309 case 3:
1310 return {{"gl_PointSize", Type::Float}}; 1310 return {{"gl_PointSize", Type::Float}};
1311 } 1311 }
1312 return {}; 1312 return std::nullopt;
1313 case Attribute::Index::FrontColor: 1313 case Attribute::Index::FrontColor:
1314 return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}}; 1314 return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}};
1315 case Attribute::Index::FrontSecondaryColor: 1315 case Attribute::Index::FrontSecondaryColor:
@@ -1332,7 +1332,7 @@ private:
1332 Type::Float}}; 1332 Type::Float}};
1333 } 1333 }
1334 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute)); 1334 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
1335 return {}; 1335 return std::nullopt;
1336 } 1336 }
1337 } 1337 }
1338 1338
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a4c5b8f74..2ccca1993 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -32,20 +32,6 @@ namespace OpenGL {
32 32
33namespace { 33namespace {
34 34
35constexpr std::size_t SWAP_CHAIN_SIZE = 3;
36
37struct Frame {
38 u32 width{}; /// Width of the frame (to detect resize)
39 u32 height{}; /// Height of the frame
40 bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
41 OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
42 OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
43 OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
44 GLsync render_fence{}; /// Fence created on the render thread
45 GLsync present_fence{}; /// Fence created on the presentation thread
46 bool is_srgb{}; /// Framebuffer is sRGB or RGB
47};
48
49constexpr GLint PositionLocation = 0; 35constexpr GLint PositionLocation = 0;
50constexpr GLint TexCoordLocation = 1; 36constexpr GLint TexCoordLocation = 1;
51constexpr GLint ModelViewMatrixLocation = 0; 37constexpr GLint ModelViewMatrixLocation = 0;
@@ -58,24 +44,6 @@ struct ScreenRectVertex {
58 std::array<GLfloat, 2> tex_coord; 44 std::array<GLfloat, 2> tex_coord;
59}; 45};
60 46
61/// Returns true if any debug tool is attached
62bool HasDebugTool() {
63 const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
64 if (nsight) {
65 return true;
66 }
67
68 GLint num_extensions;
69 glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
70 for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
71 const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
72 if (!std::strcmp(name, "GL_EXT_debug_tool")) {
73 return true;
74 }
75 }
76 return false;
77}
78
79/** 47/**
80 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left 48 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
81 * corner and (width, height) on the lower-bottom. 49 * corner and (width, height) on the lower-bottom.
@@ -159,135 +127,15 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
159 127
160} // Anonymous namespace 128} // Anonymous namespace
161 129
162/**
163 * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
164 * but also make sure that rendering happens at the pace that the frontend dictates. This is a
165 * helper class that the renderer uses to sync frames between the render thread and the presentation
166 * thread
167 */
168class FrameMailbox {
169public:
170 std::mutex swap_chain_lock;
171 std::condition_variable present_cv;
172 std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
173 std::queue<Frame*> free_queue;
174 std::deque<Frame*> present_queue;
175 Frame* previous_frame{};
176
177 FrameMailbox() {
178 for (auto& frame : swap_chain) {
179 free_queue.push(&frame);
180 }
181 }
182
183 ~FrameMailbox() {
184 // lock the mutex and clear out the present and free_queues and notify any people who are
185 // blocked to prevent deadlock on shutdown
186 std::scoped_lock lock{swap_chain_lock};
187 std::queue<Frame*>().swap(free_queue);
188 present_queue.clear();
189 present_cv.notify_all();
190 }
191
192 void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
193 frame->present.Release();
194 frame->present.Create();
195 GLint previous_draw_fbo{};
196 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
197 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
198 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
199 frame->color.handle);
200 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
201 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
202 }
203 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
204 frame->color_reloaded = false;
205 }
206
207 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
208 // Recreate the color texture attachment
209 frame->color.Release();
210 frame->color.Create();
211 const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
212 glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
213
214 // Recreate the FBO for the render target
215 frame->render.Release();
216 frame->render.Create();
217 glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
218 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
219 frame->color.handle);
220 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
221 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
222 }
223
224 frame->width = width;
225 frame->height = height;
226 frame->color_reloaded = true;
227 }
228
229 Frame* GetRenderFrame() {
230 std::unique_lock lock{swap_chain_lock};
231
232 // If theres no free frames, we will reuse the oldest render frame
233 if (free_queue.empty()) {
234 auto frame = present_queue.back();
235 present_queue.pop_back();
236 return frame;
237 }
238
239 Frame* frame = free_queue.front();
240 free_queue.pop();
241 return frame;
242 }
243
244 void ReleaseRenderFrame(Frame* frame) {
245 std::unique_lock lock{swap_chain_lock};
246 present_queue.push_front(frame);
247 present_cv.notify_one();
248 }
249
250 Frame* TryGetPresentFrame(int timeout_ms) {
251 std::unique_lock lock{swap_chain_lock};
252 // wait for new entries in the present_queue
253 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
254 [&] { return !present_queue.empty(); });
255 if (present_queue.empty()) {
256 // timed out waiting for a frame to draw so return the previous frame
257 return previous_frame;
258 }
259
260 // free the previous frame and add it back to the free queue
261 if (previous_frame) {
262 free_queue.push(previous_frame);
263 }
264
265 // the newest entries are pushed to the front of the queue
266 Frame* frame = present_queue.front();
267 present_queue.pop_front();
268 // remove all old entries from the present queue and move them back to the free_queue
269 for (auto f : present_queue) {
270 free_queue.push(f);
271 }
272 present_queue.clear();
273 previous_frame = frame;
274 return frame;
275 }
276};
277
278RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, 130RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
279 Core::Frontend::EmuWindow& emu_window_, 131 Core::Frontend::EmuWindow& emu_window_,
280 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, 132 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
281 std::unique_ptr<Core::Frontend::GraphicsContext> context) 133 std::unique_ptr<Core::Frontend::GraphicsContext> context)
282 : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_}, 134 : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_},
283 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device}, 135 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device} {}
284 has_debug_tool{HasDebugTool()} {}
285 136
286RendererOpenGL::~RendererOpenGL() = default; 137RendererOpenGL::~RendererOpenGL() = default;
287 138
288MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
289MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
290
291void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 139void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
292 if (!framebuffer) { 140 if (!framebuffer) {
293 return; 141 return;
@@ -296,79 +144,34 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
296 PrepareRendertarget(framebuffer); 144 PrepareRendertarget(framebuffer);
297 RenderScreenshot(); 145 RenderScreenshot();
298 146
299 Frame* frame; 147 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
300 { 148 DrawScreen(emu_window.GetFramebufferLayout());
301 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
302
303 frame = frame_mailbox->GetRenderFrame();
304
305 // Clean up sync objects before drawing
306
307 // INTEL driver workaround. We can't delete the previous render sync object until we are
308 // sure that the presentation is done
309 if (frame->present_fence) {
310 glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
311 }
312
313 // delete the draw fence if the frame wasn't presented
314 if (frame->render_fence) {
315 glDeleteSync(frame->render_fence);
316 frame->render_fence = 0;
317 }
318
319 // wait for the presentation to be done
320 if (frame->present_fence) {
321 glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
322 glDeleteSync(frame->present_fence);
323 frame->present_fence = 0;
324 }
325 }
326 149
327 { 150 ++m_current_frame;
328 MICROPROFILE_SCOPE(OpenGL_RenderFrame);
329 const auto& layout = render_window.GetFramebufferLayout();
330 151
331 // Recreate the frame if the size of the window has changed 152 rasterizer->TickFrame();
332 if (layout.width != frame->width || layout.height != frame->height ||
333 screen_info.display_srgb != frame->is_srgb) {
334 LOG_DEBUG(Render_OpenGL, "Reloading render frame");
335 frame->is_srgb = screen_info.display_srgb;
336 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
337 }
338 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
339 DrawScreen(layout);
340 // Create a fence for the frontend to wait on and swap this frame to OffTex
341 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
342 glFlush();
343 frame_mailbox->ReleaseRenderFrame(frame);
344 m_current_frame++;
345 rasterizer->TickFrame();
346 }
347 153
348 render_window.PollEvents(); 154 render_window.PollEvents();
349 if (has_debug_tool) { 155 context->SwapBuffers();
350 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
351 Present(0);
352 context->SwapBuffers();
353 }
354} 156}
355 157
356void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { 158void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
357 if (framebuffer) { 159 if (!framebuffer) {
358 // If framebuffer is provided, reload it from memory to a texture 160 return;
359 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || 161 }
360 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || 162 // If framebuffer is provided, reload it from memory to a texture
361 screen_info.texture.pixel_format != framebuffer->pixel_format || 163 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
362 gl_framebuffer_data.empty()) { 164 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
363 // Reallocate texture if the framebuffer size has changed. 165 screen_info.texture.pixel_format != framebuffer->pixel_format ||
364 // This is expected to not happen very often and hence should not be a 166 gl_framebuffer_data.empty()) {
365 // performance problem. 167 // Reallocate texture if the framebuffer size has changed.
366 ConfigureFramebufferTexture(screen_info.texture, *framebuffer); 168 // This is expected to not happen very often and hence should not be a
367 } 169 // performance problem.
368 170 ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
369 // Load the framebuffer from memory, draw it to the screen, and swap buffers
370 LoadFBToScreenInfo(*framebuffer);
371 } 171 }
172
173 // Load the framebuffer from memory, draw it to the screen, and swap buffers
174 LoadFBToScreenInfo(*framebuffer);
372} 175}
373 176
374void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { 177void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,8 +221,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
418} 221}
419 222
420void RendererOpenGL::InitOpenGLObjects() { 223void RendererOpenGL::InitOpenGLObjects() {
421 frame_mailbox = std::make_unique<FrameMailbox>();
422
423 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), 224 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
424 Settings::values.bg_blue.GetValue(), 0.0f); 225 Settings::values.bg_blue.GetValue(), 0.0f);
425 226
@@ -647,51 +448,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
647 program_manager.RestoreGuestPipeline(); 448 program_manager.RestoreGuestPipeline();
648} 449}
649 450
650bool RendererOpenGL::TryPresent(int timeout_ms) {
651 if (has_debug_tool) {
652 LOG_DEBUG(Render_OpenGL,
653 "Skipping presentation because we are presenting on the main context");
654 return false;
655 }
656 return Present(timeout_ms);
657}
658
659bool RendererOpenGL::Present(int timeout_ms) {
660 const auto& layout = render_window.GetFramebufferLayout();
661 auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
662 if (!frame) {
663 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
664 return false;
665 }
666
667 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
668 // readback since we won't be doing any blending
669 glClear(GL_COLOR_BUFFER_BIT);
670
671 // Recreate the presentation FBO if the color attachment was changed
672 if (frame->color_reloaded) {
673 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
674 frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
675 }
676 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
677 // INTEL workaround.
678 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
679 // it on the emulation thread without too much penalty
680 // glDeleteSync(frame.render_sync);
681 // frame.render_sync = 0;
682
683 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
684 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
685 GL_COLOR_BUFFER_BIT, GL_LINEAR);
686
687 // Insert fence for the main thread to block on
688 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
689 glFlush();
690
691 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
692 return true;
693}
694
695void RendererOpenGL::RenderScreenshot() { 451void RendererOpenGL::RenderScreenshot() {
696 if (!renderer_settings.screenshot_requested) { 452 if (!renderer_settings.screenshot_requested) {
697 return; 453 return;
@@ -706,7 +462,7 @@ void RendererOpenGL::RenderScreenshot() {
706 screenshot_framebuffer.Create(); 462 screenshot_framebuffer.Create();
707 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); 463 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
708 464
709 Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 465 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
710 466
711 GLuint renderbuffer; 467 GLuint renderbuffer;
712 glGenRenderbuffers(1, &renderbuffer); 468 glGenRenderbuffers(1, &renderbuffer);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 5329577fb..9ef181f95 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -55,14 +55,6 @@ struct ScreenInfo {
55 TextureInfo texture; 55 TextureInfo texture;
56}; 56};
57 57
58struct PresentationTexture {
59 u32 width = 0;
60 u32 height = 0;
61 OGLTexture texture;
62};
63
64class FrameMailbox;
65
66class RendererOpenGL final : public VideoCore::RendererBase { 58class RendererOpenGL final : public VideoCore::RendererBase {
67public: 59public:
68 explicit RendererOpenGL(Core::TelemetrySession& telemetry_session, 60 explicit RendererOpenGL(Core::TelemetrySession& telemetry_session,
@@ -74,7 +66,6 @@ public:
74 bool Init() override; 66 bool Init() override;
75 void ShutDown() override; 67 void ShutDown() override;
76 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 68 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
77 bool TryPresent(int timeout_ms) override;
78 69
79private: 70private:
80 /// Initializes the OpenGL state and creates persistent objects. 71 /// Initializes the OpenGL state and creates persistent objects.
@@ -102,8 +93,6 @@ private:
102 93
103 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); 94 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
104 95
105 bool Present(int timeout_ms);
106
107 Core::TelemetrySession& telemetry_session; 96 Core::TelemetrySession& telemetry_session;
108 Core::Frontend::EmuWindow& emu_window; 97 Core::Frontend::EmuWindow& emu_window;
109 Core::Memory::Memory& cpu_memory; 98 Core::Memory::Memory& cpu_memory;
@@ -134,11 +123,6 @@ private:
134 /// Used for transforming the framebuffer orientation 123 /// Used for transforming the framebuffer orientation
135 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; 124 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
136 Common::Rectangle<int> framebuffer_crop_rect; 125 Common::Rectangle<int> framebuffer_crop_rect;
137
138 /// Frame presentation mailbox
139 std::unique_ptr<FrameMailbox> frame_mailbox;
140
141 bool has_debug_tool = false;
142}; 126};
143 127
144} // namespace OpenGL 128} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 0e4583986..d38e797a4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -283,11 +283,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
283 render_window.PollEvents(); 283 render_window.PollEvents();
284} 284}
285 285
286bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
287 // TODO (bunnei): ImplementMe
288 return true;
289}
290
291bool RendererVulkan::Init() { 286bool RendererVulkan::Init() {
292 library = OpenVulkanLibrary(); 287 library = OpenVulkanLibrary();
293 instance = CreateInstance(library, dld, render_window.GetWindowInfo().type, 288 instance = CreateInstance(library, dld, render_window.GetWindowInfo().type,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index ddff77942..5085310d0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -55,7 +55,6 @@ public:
55 bool Init() override; 55 bool Init() override;
56 void ShutDown() override; 56 void ShutDown() override;
57 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 57 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
58 bool TryPresent(int timeout_ms) override;
59 58
60 static std::vector<std::string> EnumerateDevices(); 59 static std::vector<std::string> EnumerateDevices();
61 60
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
index cca13bcde..8e5a22ab3 100644
--- a/src/video_core/shader/ast.h
+++ b/src/video_core/shader/ast.h
@@ -199,55 +199,48 @@ public:
199 } 199 }
200 200
201 std::optional<u32> GetGotoLabel() const { 201 std::optional<u32> GetGotoLabel() const {
202 auto inner = std::get_if<ASTGoto>(&data); 202 if (const auto* inner = std::get_if<ASTGoto>(&data)) {
203 if (inner) {
204 return {inner->label}; 203 return {inner->label};
205 } 204 }
206 return {}; 205 return std::nullopt;
207 } 206 }
208 207
209 Expr GetGotoCondition() const { 208 Expr GetGotoCondition() const {
210 auto inner = std::get_if<ASTGoto>(&data); 209 if (const auto* inner = std::get_if<ASTGoto>(&data)) {
211 if (inner) {
212 return inner->condition; 210 return inner->condition;
213 } 211 }
214 return nullptr; 212 return nullptr;
215 } 213 }
216 214
217 void MarkLabelUnused() { 215 void MarkLabelUnused() {
218 auto inner = std::get_if<ASTLabel>(&data); 216 if (auto* inner = std::get_if<ASTLabel>(&data)) {
219 if (inner) {
220 inner->unused = true; 217 inner->unused = true;
221 } 218 }
222 } 219 }
223 220
224 bool IsLabelUnused() const { 221 bool IsLabelUnused() const {
225 auto inner = std::get_if<ASTLabel>(&data); 222 if (const auto* inner = std::get_if<ASTLabel>(&data)) {
226 if (inner) {
227 return inner->unused; 223 return inner->unused;
228 } 224 }
229 return true; 225 return true;
230 } 226 }
231 227
232 std::optional<u32> GetLabelIndex() const { 228 std::optional<u32> GetLabelIndex() const {
233 auto inner = std::get_if<ASTLabel>(&data); 229 if (const auto* inner = std::get_if<ASTLabel>(&data)) {
234 if (inner) {
235 return {inner->index}; 230 return {inner->index};
236 } 231 }
237 return {}; 232 return std::nullopt;
238 } 233 }
239 234
240 Expr GetIfCondition() const { 235 Expr GetIfCondition() const {
241 auto inner = std::get_if<ASTIfThen>(&data); 236 if (const auto* inner = std::get_if<ASTIfThen>(&data)) {
242 if (inner) {
243 return inner->condition; 237 return inner->condition;
244 } 238 }
245 return nullptr; 239 return nullptr;
246 } 240 }
247 241
248 void SetGotoCondition(Expr new_condition) { 242 void SetGotoCondition(Expr new_condition) {
249 auto inner = std::get_if<ASTGoto>(&data); 243 if (auto* inner = std::get_if<ASTGoto>(&data)) {
250 if (inner) {
251 inner->condition = std::move(new_condition); 244 inner->condition = std::move(new_condition);
252 } 245 }
253 } 246 }
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 336397cdb..4c8971615 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -547,13 +547,13 @@ bool TryQuery(CFGRebuildState& state) {
547 gather_labels(q2.ssy_stack, state.ssy_labels, block); 547 gather_labels(q2.ssy_stack, state.ssy_labels, block);
548 gather_labels(q2.pbk_stack, state.pbk_labels, block); 548 gather_labels(q2.pbk_stack, state.pbk_labels, block);
549 if (std::holds_alternative<SingleBranch>(*block.branch)) { 549 if (std::holds_alternative<SingleBranch>(*block.branch)) {
550 const auto branch = std::get_if<SingleBranch>(block.branch.get()); 550 auto* branch = std::get_if<SingleBranch>(block.branch.get());
551 if (!branch->condition.IsUnconditional()) { 551 if (!branch->condition.IsUnconditional()) {
552 q2.address = block.end + 1; 552 q2.address = block.end + 1;
553 state.queries.push_back(q2); 553 state.queries.push_back(q2);
554 } 554 }
555 555
556 Query conditional_query{q2}; 556 auto& conditional_query = state.queries.emplace_back(q2);
557 if (branch->is_sync) { 557 if (branch->is_sync) {
558 if (branch->address == unassigned_branch) { 558 if (branch->address == unassigned_branch) {
559 branch->address = conditional_query.ssy_stack.top(); 559 branch->address = conditional_query.ssy_stack.top();
@@ -567,21 +567,21 @@ bool TryQuery(CFGRebuildState& state) {
567 conditional_query.pbk_stack.pop(); 567 conditional_query.pbk_stack.pop();
568 } 568 }
569 conditional_query.address = branch->address; 569 conditional_query.address = branch->address;
570 state.queries.push_back(std::move(conditional_query));
571 return true; 570 return true;
572 } 571 }
573 const auto multi_branch = std::get_if<MultiBranch>(block.branch.get()); 572
573 const auto* multi_branch = std::get_if<MultiBranch>(block.branch.get());
574 for (const auto& branch_case : multi_branch->branches) { 574 for (const auto& branch_case : multi_branch->branches) {
575 Query conditional_query{q2}; 575 auto& conditional_query = state.queries.emplace_back(q2);
576 conditional_query.address = branch_case.address; 576 conditional_query.address = branch_case.address;
577 state.queries.push_back(std::move(conditional_query));
578 } 577 }
578
579 return true; 579 return true;
580} 580}
581 581
582void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { 582void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
583 const auto get_expr = ([&](const Condition& cond) -> Expr { 583 const auto get_expr = [](const Condition& cond) -> Expr {
584 Expr result{}; 584 Expr result;
585 if (cond.cc != ConditionCode::T) { 585 if (cond.cc != ConditionCode::T) {
586 result = MakeExpr<ExprCondCode>(cond.cc); 586 result = MakeExpr<ExprCondCode>(cond.cc);
587 } 587 }
@@ -594,10 +594,10 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
594 } 594 }
595 Expr extra = MakeExpr<ExprPredicate>(pred); 595 Expr extra = MakeExpr<ExprPredicate>(pred);
596 if (negate) { 596 if (negate) {
597 extra = MakeExpr<ExprNot>(extra); 597 extra = MakeExpr<ExprNot>(std::move(extra));
598 } 598 }
599 if (result) { 599 if (result) {
600 return MakeExpr<ExprAnd>(extra, result); 600 return MakeExpr<ExprAnd>(std::move(extra), std::move(result));
601 } 601 }
602 return extra; 602 return extra;
603 } 603 }
@@ -605,9 +605,10 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
605 return result; 605 return result;
606 } 606 }
607 return MakeExpr<ExprBoolean>(true); 607 return MakeExpr<ExprBoolean>(true);
608 }); 608 };
609
609 if (std::holds_alternative<SingleBranch>(*branch_info)) { 610 if (std::holds_alternative<SingleBranch>(*branch_info)) {
610 const auto branch = std::get_if<SingleBranch>(branch_info.get()); 611 const auto* branch = std::get_if<SingleBranch>(branch_info.get());
611 if (branch->address < 0) { 612 if (branch->address < 0) {
612 if (branch->kill) { 613 if (branch->kill) {
613 mm.InsertReturn(get_expr(branch->condition), true); 614 mm.InsertReturn(get_expr(branch->condition), true);
@@ -619,7 +620,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
619 mm.InsertGoto(get_expr(branch->condition), branch->address); 620 mm.InsertGoto(get_expr(branch->condition), branch->address);
620 return; 621 return;
621 } 622 }
622 const auto multi_branch = std::get_if<MultiBranch>(branch_info.get()); 623 const auto* multi_branch = std::get_if<MultiBranch>(branch_info.get());
623 for (const auto& branch_case : multi_branch->branches) { 624 for (const auto& branch_case : multi_branch->branches) {
624 mm.InsertGoto(MakeExpr<ExprGprEqual>(multi_branch->gpr, branch_case.cmp_value), 625 mm.InsertGoto(MakeExpr<ExprGprEqual>(multi_branch->gpr, branch_case.cmp_value),
625 branch_case.address); 626 branch_case.address);
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index d5ed81442..6be3ea92b 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -205,12 +205,12 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
205 const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1); 205 const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1);
206 const auto& found = result.first; 206 const auto& found = result.first;
207 if (!found) { 207 if (!found) {
208 return {}; 208 return std::nullopt;
209 } 209 }
210 if (const auto immediate = std::get_if<ImmediateNode>(&*found)) { 210 if (const auto immediate = std::get_if<ImmediateNode>(&*found)) {
211 return immediate->GetValue(); 211 return immediate->GetValue();
212 } 212 }
213 return {}; 213 return std::nullopt;
214} 214}
215 215
216std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, 216std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index dfcf36e0b..b44c09d71 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -115,20 +115,24 @@ std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap(
115 if (gpu_addr == candidate_gpu_addr) { 115 if (gpu_addr == candidate_gpu_addr) {
116 return {{0, 0}}; 116 return {{0, 0}};
117 } 117 }
118
118 if (candidate_gpu_addr < gpu_addr) { 119 if (candidate_gpu_addr < gpu_addr) {
119 return {}; 120 return std::nullopt;
120 } 121 }
122
121 const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; 123 const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)};
122 const auto layer{static_cast<u32>(relative_address / layer_size)}; 124 const auto layer{static_cast<u32>(relative_address / layer_size)};
123 if (layer >= params.depth) { 125 if (layer >= params.depth) {
124 return {}; 126 return std::nullopt;
125 } 127 }
128
126 const GPUVAddr mipmap_address = relative_address - layer_size * layer; 129 const GPUVAddr mipmap_address = relative_address - layer_size * layer;
127 const auto mipmap_it = 130 const auto mipmap_it =
128 Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); 131 Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
129 if (mipmap_it == mipmap_offsets.end()) { 132 if (mipmap_it == mipmap_offsets.end()) {
130 return {}; 133 return std::nullopt;
131 } 134 }
135
132 const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))}; 136 const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))};
133 return std::make_pair(layer, level); 137 return std::make_pair(layer, level);
134} 138}
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index caa2d06d3..408eac2b7 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -218,15 +218,6 @@ public:
218 218
219 virtual ~RenderWidget() = default; 219 virtual ~RenderWidget() = default;
220 220
221 /// Called on the UI thread when this Widget is ready to draw
222 /// Dervied classes can override this to draw the latest frame.
223 virtual void Present() {}
224
225 void paintEvent(QPaintEvent* event) override {
226 Present();
227 update();
228 }
229
230 QPaintEngine* paintEngine() const override { 221 QPaintEngine* paintEngine() const override {
231 return nullptr; 222 return nullptr;
232 } 223 }
@@ -245,20 +236,8 @@ public:
245 context = std::move(context_); 236 context = std::move(context_);
246 } 237 }
247 238
248 void Present() override {
249 if (!isVisible()) {
250 return;
251 }
252
253 context->MakeCurrent();
254 if (Core::System::GetInstance().Renderer().TryPresent(100)) {
255 context->SwapBuffers();
256 glFinish();
257 }
258 }
259
260private: 239private:
261 std::unique_ptr<Core::Frontend::GraphicsContext> context{}; 240 std::unique_ptr<Core::Frontend::GraphicsContext> context;
262}; 241};
263 242
264#ifdef HAS_VULKAN 243#ifdef HAS_VULKAN
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 6a71d9644..a9738e298 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -56,7 +56,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
56 case Qt::Key_Return: 56 case Qt::Key_Return:
57 case Qt::Key_Enter: { 57 case Qt::Key_Enter: {
58 if (gamelist->search_field->visible == 1) { 58 if (gamelist->search_field->visible == 1) {
59 QString file_path = gamelist->getLastFilterResultItem(); 59 const QString file_path = gamelist->GetLastFilterResultItem();
60 60
61 // To avoid loading error dialog loops while confirming them using enter 61 // To avoid loading error dialog loops while confirming them using enter
62 // Also users usually want to run a different game after closing one 62 // Also users usually want to run a different game after closing one
@@ -83,22 +83,25 @@ void GameListSearchField::setFilterResult(int visible, int total) {
83 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); 83 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
84} 84}
85 85
86QString GameList::getLastFilterResultItem() const { 86QString GameList::GetLastFilterResultItem() const {
87 QStandardItem* folder;
88 QStandardItem* child;
89 QString file_path; 87 QString file_path;
90 const int folder_count = item_model->rowCount(); 88 const int folder_count = item_model->rowCount();
89
91 for (int i = 0; i < folder_count; ++i) { 90 for (int i = 0; i < folder_count; ++i) {
92 folder = item_model->item(i, 0); 91 const QStandardItem* folder = item_model->item(i, 0);
93 const QModelIndex folder_index = folder->index(); 92 const QModelIndex folder_index = folder->index();
94 const int children_count = folder->rowCount(); 93 const int children_count = folder->rowCount();
94
95 for (int j = 0; j < children_count; ++j) { 95 for (int j = 0; j < children_count; ++j) {
96 if (!tree_view->isRowHidden(j, folder_index)) { 96 if (tree_view->isRowHidden(j, folder_index)) {
97 child = folder->child(j, 0); 97 continue;
98 file_path = child->data(GameListItemPath::FullPathRole).toString();
99 } 98 }
99
100 const QStandardItem* child = folder->child(j, 0);
101 file_path = child->data(GameListItemPath::FullPathRole).toString();
100 } 102 }
101 } 103 }
104
102 return file_path; 105 return file_path;
103} 106}
104 107
@@ -123,7 +126,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
123 edit_filter->setPlaceholderText(tr("Enter pattern to filter")); 126 edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
124 edit_filter->installEventFilter(key_release_eater); 127 edit_filter->installEventFilter(key_release_eater);
125 edit_filter->setClearButtonEnabled(true); 128 edit_filter->setClearButtonEnabled(true);
126 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged); 129 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged);
127 label_filter_result = new QLabel; 130 label_filter_result = new QLabel;
128 button_filter_close = new QToolButton(this); 131 button_filter_close = new QToolButton(this);
129 button_filter_close->setText(QStringLiteral("X")); 132 button_filter_close->setText(QStringLiteral("X"));
@@ -133,7 +136,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
133 "#000000; font-weight: bold; background: #F0F0F0; }" 136 "#000000; font-weight: bold; background: #F0F0F0; }"
134 "QToolButton:hover{ border: none; padding: 0px; color: " 137 "QToolButton:hover{ border: none; padding: 0px; color: "
135 "#EEEEEE; font-weight: bold; background: #E81123}")); 138 "#EEEEEE; font-weight: bold; background: #E81123}"));
136 connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked); 139 connect(button_filter_close, &QToolButton::clicked, parent, &GameList::OnFilterCloseClicked);
137 layout_filter->setSpacing(10); 140 layout_filter->setSpacing(10);
138 layout_filter->addWidget(label_filter); 141 layout_filter->addWidget(label_filter);
139 layout_filter->addWidget(edit_filter); 142 layout_filter->addWidget(edit_filter);
@@ -159,16 +162,22 @@ static bool ContainsAllWords(const QString& haystack, const QString& userinput)
159} 162}
160 163
161// Syncs the expanded state of Game Directories with settings to persist across sessions 164// Syncs the expanded state of Game Directories with settings to persist across sessions
162void GameList::onItemExpanded(const QModelIndex& item) { 165void GameList::OnItemExpanded(const QModelIndex& item) {
163 const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>(); 166 const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>();
164 if (type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || 167 const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
165 type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir) 168 type == GameListItemType::UserNandDir ||
166 item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded = 169 type == GameListItemType::SysNandDir;
167 tree_view->isExpanded(item); 170
171 if (!is_dir) {
172 return;
173 }
174
175 auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
176 game_dir->expanded = tree_view->isExpanded(item);
168} 177}
169 178
170// Event in order to filter the gamelist after editing the searchfield 179// Event in order to filter the gamelist after editing the searchfield
171void GameList::onTextChanged(const QString& new_text) { 180void GameList::OnTextChanged(const QString& new_text) {
172 const int folder_count = tree_view->model()->rowCount(); 181 const int folder_count = tree_view->model()->rowCount();
173 QString edit_filter_text = new_text.toLower(); 182 QString edit_filter_text = new_text.toLower();
174 QStandardItem* folder; 183 QStandardItem* folder;
@@ -224,7 +233,7 @@ void GameList::onTextChanged(const QString& new_text) {
224 } 233 }
225} 234}
226 235
227void GameList::onUpdateThemedIcons() { 236void GameList::OnUpdateThemedIcons() {
228 for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { 237 for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
229 QStandardItem* child = item_model->invisibleRootItem()->child(i); 238 QStandardItem* child = item_model->invisibleRootItem()->child(i);
230 239
@@ -276,7 +285,7 @@ void GameList::onUpdateThemedIcons() {
276 } 285 }
277} 286}
278 287
279void GameList::onFilterCloseClicked() { 288void GameList::OnFilterCloseClicked() {
280 main_window->filterBarSetChecked(false); 289 main_window->filterBarSetChecked(false);
281} 290}
282 291
@@ -317,11 +326,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
317 } 326 }
318 item_model->setSortRole(GameListItemPath::SortRole); 327 item_model->setSortRole(GameListItemPath::SortRole);
319 328
320 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); 329 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
321 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 330 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
322 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 331 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
323 connect(tree_view, &QTreeView::expanded, this, &GameList::onItemExpanded); 332 connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
324 connect(tree_view, &QTreeView::collapsed, this, &GameList::onItemExpanded); 333 connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
325 334
326 // We must register all custom types with the Qt Automoc system so that we are able to use 335 // We must register all custom types with the Qt Automoc system so that we are able to use
327 // it with signals/slots. In this case, QList falls under the umbrells of custom types. 336 // it with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -338,17 +347,17 @@ GameList::~GameList() {
338 emit ShouldCancelWorker(); 347 emit ShouldCancelWorker();
339} 348}
340 349
341void GameList::setFilterFocus() { 350void GameList::SetFilterFocus() {
342 if (tree_view->model()->rowCount() > 0) { 351 if (tree_view->model()->rowCount() > 0) {
343 search_field->setFocus(); 352 search_field->setFocus();
344 } 353 }
345} 354}
346 355
347void GameList::setFilterVisible(bool visibility) { 356void GameList::SetFilterVisible(bool visibility) {
348 search_field->setVisible(visibility); 357 search_field->setVisible(visibility);
349} 358}
350 359
351void GameList::clearFilter() { 360void GameList::ClearFilter() {
352 search_field->clear(); 361 search_field->clear();
353} 362}
354 363
@@ -397,10 +406,11 @@ void GameList::ValidateEntry(const QModelIndex& item) {
397 } 406 }
398} 407}
399 408
400bool GameList::isEmpty() const { 409bool GameList::IsEmpty() const {
401 for (int i = 0; i < item_model->rowCount(); i++) { 410 for (int i = 0; i < item_model->rowCount(); i++) {
402 const QStandardItem* child = item_model->invisibleRootItem()->child(i); 411 const QStandardItem* child = item_model->invisibleRootItem()->child(i);
403 const auto type = static_cast<GameListItemType>(child->type()); 412 const auto type = static_cast<GameListItemType>(child->type());
413
404 if (!child->hasChildren() && 414 if (!child->hasChildren() &&
405 (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || 415 (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir ||
406 type == GameListItemType::SysNandDir)) { 416 type == GameListItemType::SysNandDir)) {
@@ -408,11 +418,12 @@ bool GameList::isEmpty() const {
408 i--; 418 i--;
409 } 419 }
410 } 420 }
421
411 return !item_model->invisibleRootItem()->hasChildren(); 422 return !item_model->invisibleRootItem()->hasChildren();
412} 423}
413 424
414void GameList::DonePopulating(QStringList watch_list) { 425void GameList::DonePopulating(const QStringList& watch_list) {
415 emit ShowList(!isEmpty()); 426 emit ShowList(!IsEmpty());
416 427
417 item_model->invisibleRootItem()->appendRow(new GameListAddDir()); 428 item_model->invisibleRootItem()->appendRow(new GameListAddDir());
418 429
@@ -472,7 +483,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
472 context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); 483 context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
473} 484}
474 485
475void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { 486void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
476 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 487 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
477 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); 488 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
478 QAction* open_transferable_shader_cache = 489 QAction* open_transferable_shader_cache =
@@ -690,12 +701,15 @@ void GameList::SaveInterfaceLayout() {
690} 701}
691 702
692void GameList::LoadInterfaceLayout() { 703void GameList::LoadInterfaceLayout() {
693 auto header = tree_view->header(); 704 auto* header = tree_view->header();
694 if (!header->restoreState(UISettings::values.gamelist_header_state)) { 705
695 // We are using the name column to display icons and titles 706 if (header->restoreState(UISettings::values.gamelist_header_state)) {
696 // so make it as large as possible as default. 707 return;
697 header->resizeSection(COLUMN_NAME, header->width());
698 } 708 }
709
710 // We are using the name column to display icons and titles
711 // so make it as large as possible as default.
712 header->resizeSection(COLUMN_NAME, header->width());
699} 713}
700 714
701const QStringList GameList::supported_file_extensions = { 715const QStringList GameList::supported_file_extensions = {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 78e2ba169..58059a3c4 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -67,11 +67,11 @@ public:
67 FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr); 67 FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);
68 ~GameList() override; 68 ~GameList() override;
69 69
70 QString getLastFilterResultItem() const; 70 QString GetLastFilterResultItem() const;
71 void clearFilter(); 71 void ClearFilter();
72 void setFilterFocus(); 72 void SetFilterFocus();
73 void setFilterVisible(bool visibility); 73 void SetFilterVisible(bool visibility);
74 bool isEmpty() const; 74 bool IsEmpty() const;
75 75
76 void LoadCompatibilityList(); 76 void LoadCompatibilityList();
77 void PopulateAsync(QVector<UISettings::GameDir>& game_dirs); 77 void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
@@ -82,7 +82,7 @@ public:
82 static const QStringList supported_file_extensions; 82 static const QStringList supported_file_extensions;
83 83
84signals: 84signals:
85 void GameChosen(QString game_path); 85 void GameChosen(const QString& game_path);
86 void ShouldCancelWorker(); 86 void ShouldCancelWorker();
87 void OpenFolderRequested(u64 program_id, GameListOpenTarget target, 87 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
88 const std::string& game_path); 88 const std::string& game_path);
@@ -99,21 +99,21 @@ signals:
99 void ShowList(bool show); 99 void ShowList(bool show);
100 100
101private slots: 101private slots:
102 void onItemExpanded(const QModelIndex& item); 102 void OnItemExpanded(const QModelIndex& item);
103 void onTextChanged(const QString& new_text); 103 void OnTextChanged(const QString& new_text);
104 void onFilterCloseClicked(); 104 void OnFilterCloseClicked();
105 void onUpdateThemedIcons(); 105 void OnUpdateThemedIcons();
106 106
107private: 107private:
108 void AddDirEntry(GameListDir* entry_items); 108 void AddDirEntry(GameListDir* entry_items);
109 void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent); 109 void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
110 void ValidateEntry(const QModelIndex& item); 110 void ValidateEntry(const QModelIndex& item);
111 void DonePopulating(QStringList watch_list); 111 void DonePopulating(const QStringList& watch_list);
112 112
113 void RefreshGameDirectory(); 113 void RefreshGameDirectory();
114 114
115 void PopupContextMenu(const QPoint& menu_location); 115 void PopupContextMenu(const QPoint& menu_location);
116 void AddGamePopup(QMenu& context_menu, u64 program_id, std::string path); 116 void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path);
117 void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected); 117 void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
118 void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); 118 void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
119 119
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h
index e4aba1b06..68e03fe4e 100644
--- a/src/yuzu/install_dialog.h
+++ b/src/yuzu/install_dialog.h
@@ -20,9 +20,8 @@ public:
20 explicit InstallDialog(QWidget* parent, const QStringList& files); 20 explicit InstallDialog(QWidget* parent, const QStringList& files);
21 ~InstallDialog() override; 21 ~InstallDialog() override;
22 22
23 QStringList GetFiles() const; 23 [[nodiscard]] QStringList GetFiles() const;
24 bool ShouldOverwriteFiles() const; 24 [[nodiscard]] int GetMinimumWidth() const;
25 int GetMinimumWidth() const;
26 25
27private: 26private:
28 QListWidget* file_list; 27 QListWidget* file_list;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bb3a08ac7..6a2a88dd8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -838,7 +838,7 @@ void GMainWindow::RestoreUIState() {
838 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked()); 838 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
839 839
840 ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar); 840 ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar);
841 game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); 841 game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
842 842
843 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); 843 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
844 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); 844 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
@@ -1199,11 +1199,12 @@ void GMainWindow::ShutdownGame() {
1199 render_window->hide(); 1199 render_window->hide();
1200 loading_screen->hide(); 1200 loading_screen->hide();
1201 loading_screen->Clear(); 1201 loading_screen->Clear();
1202 if (game_list->isEmpty()) 1202 if (game_list->IsEmpty()) {
1203 game_list_placeholder->show(); 1203 game_list_placeholder->show();
1204 else 1204 } else {
1205 game_list->show(); 1205 game_list->show();
1206 game_list->setFilterFocus(); 1206 }
1207 game_list->SetFilterFocus();
1207 1208
1208 setMouseTracking(false); 1209 setMouseTracking(false);
1209 ui.centralwidget->setMouseTracking(false); 1210 ui.centralwidget->setMouseTracking(false);
@@ -2361,11 +2362,11 @@ void GMainWindow::OnAbout() {
2361} 2362}
2362 2363
2363void GMainWindow::OnToggleFilterBar() { 2364void GMainWindow::OnToggleFilterBar() {
2364 game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); 2365 game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
2365 if (ui.action_Show_Filter_Bar->isChecked()) { 2366 if (ui.action_Show_Filter_Bar->isChecked()) {
2366 game_list->setFilterFocus(); 2367 game_list->SetFilterFocus();
2367 } else { 2368 } else {
2368 game_list->clearFilter(); 2369 game_list->ClearFilter();
2369 } 2370 }
2370} 2371}
2371 2372
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index a804d5185..521209622 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,9 +13,8 @@
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(Core::System& system, bool fullscreen, 16EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
17 InputCommon::InputSubsystem* input_subsystem_) 17 : input_subsystem{input_subsystem_} {
18 : system{system}, input_subsystem{input_subsystem_} {
19 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 18 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
20 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 19 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
21 exit(1); 20 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 82750ffec..53d756c3c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -20,8 +20,7 @@ class InputSubsystem;
20 20
21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
22public: 22public:
23 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen, 23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
24 InputCommon::InputSubsystem* input_subsystem);
25 ~EmuWindow_SDL2(); 24 ~EmuWindow_SDL2();
26 25
27 /// Polls window events 26 /// Polls window events
@@ -33,9 +32,6 @@ public:
33 /// Returns if window is shown (not minimized) 32 /// Returns if window is shown (not minimized)
34 bool IsShown() const override; 33 bool IsShown() const override;
35 34
36 /// Presents the next frame
37 virtual void Present() = 0;
38
39protected: 35protected:
40 /// Called by PollEvents when a key is pressed or released. 36 /// Called by PollEvents when a key is pressed or released.
41 void OnKeyEvent(int key, u8 state); 37 void OnKeyEvent(int key, u8 state);
@@ -67,9 +63,6 @@ protected:
67 /// Called when a configuration change affects the minimal size of the window 63 /// Called when a configuration change affects the minimal size of the window
68 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; 64 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
69 65
70 /// Instance of the system, used to access renderer for the presentation thread
71 Core::System& system;
72
73 /// Is the window still open? 66 /// Is the window still open?
74 bool is_open = true; 67 bool is_open = true;
75 68
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 881b67a76..5f35233b5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -87,9 +87,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
87 return unsupported_ext.empty(); 87 return unsupported_ext.empty();
88} 88}
89 89
90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen, 90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
91 InputCommon::InputSubsystem* input_subsystem) 91 : EmuWindow_SDL2{input_subsystem} {
92 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
95 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -163,13 +162,3 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
163std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { 162std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
164 return std::make_unique<SDLGLContext>(); 163 return std::make_unique<SDLGLContext>();
165} 164}
166
167void EmuWindow_SDL2_GL::Present() {
168 SDL_GL_MakeCurrent(render_window, window_context);
169 SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0);
170 while (IsOpen()) {
171 system.Renderer().TryPresent(100);
172 SDL_GL_SwapWindow(render_window);
173 }
174 SDL_GL_MakeCurrent(render_window, nullptr);
175}
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 732a64edd..dba5c293c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -14,12 +14,9 @@ class InputSubsystem;
14 14
15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
16public: 16public:
17 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen, 17 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
18 InputCommon::InputSubsystem* input_subsystem);
19 ~EmuWindow_SDL2_GL(); 18 ~EmuWindow_SDL2_GL();
20 19
21 void Present() override;
22
23 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 20 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
24 21
25private: 22private:
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 53491f86e..3ba657c00 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -19,9 +19,8 @@
19#include <SDL.h> 19#include <SDL.h>
20#include <SDL_syswm.h> 20#include <SDL_syswm.h>
21 21
22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, 22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem)
23 InputCommon::InputSubsystem* input_subsystem) 23 : EmuWindow_SDL2{input_subsystem} {
24 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
25 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 24 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
26 Common::g_scm_branch, Common::g_scm_desc); 25 Common::g_scm_branch, Common::g_scm_desc);
27 render_window = 26 render_window =
@@ -74,7 +73,3 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
74std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { 73std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
75 return std::make_unique<DummyContext>(); 74 return std::make_unique<DummyContext>();
76} 75}
77
78void EmuWindow_SDL2_VK::Present() {
79 // TODO (bunnei): ImplementMe
80}
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 f99704d4c..bdfdc3c6f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -19,11 +19,8 @@ class InputSubsystem;
19 19
20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
21public: 21public:
22 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, 22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem);
23 InputCommon::InputSubsystem* input_subsystem); 23 ~EmuWindow_SDL2_VK() override;
24 ~EmuWindow_SDL2_VK();
25
26 void Present() override;
27 24
28 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
29}; 26};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index e960b5413..3a76c785f 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -185,11 +185,11 @@ int main(int argc, char** argv) {
185 std::unique_ptr<EmuWindow_SDL2> emu_window; 185 std::unique_ptr<EmuWindow_SDL2> emu_window;
186 switch (Settings::values.renderer_backend.GetValue()) { 186 switch (Settings::values.renderer_backend.GetValue()) {
187 case Settings::RendererBackend::OpenGL: 187 case Settings::RendererBackend::OpenGL:
188 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem); 188 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
189 break; 189 break;
190 case Settings::RendererBackend::Vulkan: 190 case Settings::RendererBackend::Vulkan:
191#ifdef HAS_VULKAN 191#ifdef HAS_VULKAN
192 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem); 192 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem);
193 break; 193 break;
194#else 194#else
195 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 195 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -240,14 +240,11 @@ int main(int argc, char** argv) {
240 system.CurrentProcess()->GetTitleID(), false, 240 system.CurrentProcess()->GetTitleID(), false,
241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
242 242
243 std::thread render_thread([&emu_window] { emu_window->Present(); });
244 system.Run(); 243 system.Run();
245 while (emu_window->IsOpen()) { 244 while (emu_window->IsOpen()) {
246 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 245 std::this_thread::sleep_for(std::chrono::milliseconds(1));
247 } 246 }
248 system.Pause(); 247 system.Pause();
249 render_thread.join();
250
251 system.Shutdown(); 248 system.Shutdown();
252 249
253 detached_tasks.WaitForAllTasks(); 250 detached_tasks.WaitForAllTasks();