summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/audio_renderer.cpp99
-rw-r--r--src/audio_core/audio_renderer.h10
-rw-r--r--src/audio_core/common.h1
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/time_zone.cpp49
-rw-r--r--src/common/time_zone.h18
-rw-r--r--src/core/file_sys/control_metadata.cpp4
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/savedata_factory.cpp9
-rw-r--r--src/core/frontend/emu_window.cpp2
-rw-r--r--src/core/frontend/framebuffer_layout.h5
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp38
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp11
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h10
-rw-r--r--src/core/hle/service/time/time_manager.cpp11
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp24
-rw-r--r--src/core/settings.cpp16
-rw-r--r--src/core/settings.h4
-rw-r--r--src/video_core/dma_pusher.cpp9
-rw-r--r--src/video_core/dma_pusher.h1
-rw-r--r--src/video_core/engines/shader_bytecode.h28
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp110
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp1
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp27
-rw-r--r--src/video_core/shader/decode.cpp2
-rw-r--r--src/video_core/shader/decode/xmad.cpp12
-rw-r--r--src/video_core/shader/node.h21
-rw-r--r--src/video_core/shader/shader_ir.cpp109
-rw-r--r--src/yuzu/bootmanager.cpp16
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_system.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.h1
-rw-r--r--src/yuzu/configuration/configure_system.ui257
-rw-r--r--src/yuzu/game_list.cpp8
-rw-r--r--src/yuzu/game_list.h2
-rw-r--r--src/yuzu/loading_screen.cpp3
-rw-r--r--src/yuzu/main.cpp85
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/main.ui12
-rw-r--r--src/yuzu_cmd/config.cpp5
-rw-r--r--src/yuzu_cmd/default_ini.h4
47 files changed, 825 insertions, 223 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index d18ef6940..50846a854 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -17,7 +17,7 @@ namespace AudioCore {
17 17
18constexpr u32 STREAM_SAMPLE_RATE{48000}; 18constexpr u32 STREAM_SAMPLE_RATE{48000};
19constexpr u32 STREAM_NUM_CHANNELS{2}; 19constexpr u32 STREAM_NUM_CHANNELS{2};
20 20using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>;
21class AudioRenderer::VoiceState { 21class AudioRenderer::VoiceState {
22public: 22public:
23 bool IsPlaying() const { 23 bool IsPlaying() const {
@@ -37,9 +37,10 @@ public:
37 } 37 }
38 38
39 void SetWaveIndex(std::size_t index); 39 void SetWaveIndex(std::size_t index);
40 std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory); 40 std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory,
41 const VoiceChannelHolder& voice_resources);
41 void UpdateState(); 42 void UpdateState();
42 void RefreshBuffer(Core::Memory::Memory& memory); 43 void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources);
43 44
44private: 45private:
45 bool is_in_use{}; 46 bool is_in_use{};
@@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
79 std::shared_ptr<Kernel::WritableEvent> buffer_event, 80 std::shared_ptr<Kernel::WritableEvent> buffer_event,
80 std::size_t instance_number) 81 std::size_t instance_number)
81 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), 82 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
82 effects(params.effect_count), memory{memory_} { 83 voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} {
83 behavior_info.SetUserRevision(params.revision); 84 behavior_info.SetUserRevision(params.revision);
84 audio_out = std::make_unique<AudioCore::AudioOut>(); 85 audio_out = std::make_unique<AudioCore::AudioOut>();
85 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, 86 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
@@ -127,6 +128,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<
127 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, 128 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
128 memory_pool_count * sizeof(MemoryPoolInfo)); 129 memory_pool_count * sizeof(MemoryPoolInfo));
129 130
131 // Copy voice resources
132 const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size +
133 config.memory_pools_size};
134 std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset,
135 sizeof(VoiceResourceInformation) * voice_resources.size());
136
130 // Copy VoiceInfo structs 137 // Copy VoiceInfo structs
131 std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + 138 std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
132 config.memory_pools_size + config.voice_resource_size}; 139 config.memory_pools_size + config.voice_resource_size};
@@ -220,14 +227,15 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
220 is_refresh_pending = true; 227 is_refresh_pending = true;
221} 228}
222 229
223std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count, 230std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(
224 Core::Memory::Memory& memory) { 231 std::size_t sample_count, Core::Memory::Memory& memory,
232 const VoiceChannelHolder& voice_resources) {
225 if (!IsPlaying()) { 233 if (!IsPlaying()) {
226 return {}; 234 return {};
227 } 235 }
228 236
229 if (is_refresh_pending) { 237 if (is_refresh_pending) {
230 RefreshBuffer(memory); 238 RefreshBuffer(memory, voice_resources);
231 } 239 }
232 240
233 const std::size_t max_size{samples.size() - offset}; 241 const std::size_t max_size{samples.size() - offset};
@@ -271,7 +279,8 @@ void AudioRenderer::VoiceState::UpdateState() {
271 is_in_use = info.is_in_use; 279 is_in_use = info.is_in_use;
272} 280}
273 281
274void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { 282void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory,
283 const VoiceChannelHolder& voice_resources) {
275 const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; 284 const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
276 const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; 285 const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
277 std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); 286 std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
@@ -296,17 +305,77 @@ void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) {
296 } 305 }
297 306
298 switch (info.channel_count) { 307 switch (info.channel_count) {
299 case 1: 308 case 1: {
300 // 1 channel is upsampled to 2 channel 309 // 1 channel is upsampled to 2 channel
301 samples.resize(new_samples.size() * 2); 310 samples.resize(new_samples.size() * 2);
311
302 for (std::size_t index = 0; index < new_samples.size(); ++index) { 312 for (std::size_t index = 0; index < new_samples.size(); ++index) {
303 samples[index * 2] = new_samples[index]; 313 auto sample = static_cast<float>(new_samples[index]);
304 samples[index * 2 + 1] = new_samples[index]; 314 if (voice_resources[0]->in_use) {
315 sample *= voice_resources[0]->mix_volumes[0];
316 }
317
318 samples[index * 2] = static_cast<s16>(sample * info.volume);
319 samples[index * 2 + 1] = static_cast<s16>(sample * info.volume);
305 } 320 }
306 break; 321 break;
322 }
307 case 2: { 323 case 2: {
308 // 2 channel is played as is 324 // 2 channel is played as is
309 samples = std::move(new_samples); 325 samples = std::move(new_samples);
326 const std::size_t sample_count = (samples.size() / 2);
327 for (std::size_t index = 0; index < sample_count; ++index) {
328 const std::size_t index_l = index * 2;
329 const std::size_t index_r = index * 2 + 1;
330
331 auto sample_l = static_cast<float>(samples[index_l]);
332 auto sample_r = static_cast<float>(samples[index_r]);
333
334 if (voice_resources[0]->in_use) {
335 sample_l *= voice_resources[0]->mix_volumes[0];
336 }
337
338 if (voice_resources[1]->in_use) {
339 sample_r *= voice_resources[1]->mix_volumes[1];
340 }
341
342 samples[index_l] = static_cast<s16>(sample_l * info.volume);
343 samples[index_r] = static_cast<s16>(sample_r * info.volume);
344 }
345 break;
346 }
347 case 6: {
348 samples.resize((new_samples.size() / 6) * 2);
349 const std::size_t sample_count = samples.size() / 2;
350
351 for (std::size_t index = 0; index < sample_count; ++index) {
352 auto FL = static_cast<float>(new_samples[index * 6]);
353 auto FR = static_cast<float>(new_samples[index * 6 + 1]);
354 auto FC = static_cast<float>(new_samples[index * 6 + 2]);
355 auto BL = static_cast<float>(new_samples[index * 6 + 4]);
356 auto BR = static_cast<float>(new_samples[index * 6 + 5]);
357
358 if (voice_resources[0]->in_use) {
359 FL *= voice_resources[0]->mix_volumes[0];
360 }
361 if (voice_resources[1]->in_use) {
362 FR *= voice_resources[1]->mix_volumes[1];
363 }
364 if (voice_resources[2]->in_use) {
365 FC *= voice_resources[2]->mix_volumes[2];
366 }
367 if (voice_resources[4]->in_use) {
368 BL *= voice_resources[4]->mix_volumes[4];
369 }
370 if (voice_resources[5]->in_use) {
371 BR *= voice_resources[5]->mix_volumes[5];
372 }
373
374 samples[index * 2] =
375 static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume);
376 samples[index * 2 + 1] =
377 static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume);
378 }
310 break; 379 break;
311 } 380 }
312 default: 381 default:
@@ -352,11 +421,17 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
352 if (!voice.IsPlaying()) { 421 if (!voice.IsPlaying()) {
353 continue; 422 continue;
354 } 423 }
424 VoiceChannelHolder resources{};
425 for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) {
426 const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel];
427 resources[channel] = &voice_resources[channel_resource_id];
428 }
355 429
356 std::size_t offset{}; 430 std::size_t offset{};
357 s64 samples_remaining{BUFFER_SIZE}; 431 s64 samples_remaining{BUFFER_SIZE};
358 while (samples_remaining > 0) { 432 while (samples_remaining > 0) {
359 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)}; 433 const std::vector<s16> samples{
434 voice.DequeueSamples(samples_remaining, memory, resources)};
360 435
361 if (samples.empty()) { 436 if (samples.empty()) {
362 break; 437 break;
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index b42770fae..1f9114c07 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10 10
11#include "audio_core/behavior_info.h" 11#include "audio_core/behavior_info.h"
12#include "audio_core/common.h"
12#include "audio_core/stream.h" 13#include "audio_core/stream.h"
13#include "common/common_funcs.h" 14#include "common/common_funcs.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
@@ -116,6 +117,14 @@ struct WaveBuffer {
116}; 117};
117static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); 118static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
118 119
120struct VoiceResourceInformation {
121 s32_le id{};
122 std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{};
123 bool in_use{};
124 INSERT_PADDING_BYTES(11);
125};
126static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size");
127
119struct VoiceInfo { 128struct VoiceInfo {
120 u32_le id; 129 u32_le id;
121 u32_le node_id; 130 u32_le node_id;
@@ -244,6 +253,7 @@ private:
244 AudioRendererParameter worker_params; 253 AudioRendererParameter worker_params;
245 std::shared_ptr<Kernel::WritableEvent> buffer_event; 254 std::shared_ptr<Kernel::WritableEvent> buffer_event;
246 std::vector<VoiceState> voices; 255 std::vector<VoiceState> voices;
256 std::vector<VoiceResourceInformation> voice_resources;
247 std::vector<EffectState> effects; 257 std::vector<EffectState> effects;
248 std::unique_ptr<AudioOut> audio_out; 258 std::unique_ptr<AudioOut> audio_out;
249 StreamPtr stream; 259 StreamPtr stream;
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 98478b66b..7bb145c53 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -14,6 +14,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
14} 14}
15 15
16constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); 16constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8');
17constexpr std::size_t MAX_MIX_BUFFERS = 24;
17 18
18static constexpr u32 VersionFromRevision(u32_le rev) { 19static constexpr u32 VersionFromRevision(u32_le rev) {
19 // "REV7" -> 7 20 // "REV7" -> 7
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index d1ec8ff08..e6769a5f3 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -148,6 +148,8 @@ add_library(common STATIC
148 thread.h 148 thread.h
149 thread_queue_list.h 149 thread_queue_list.h
150 threadsafe_queue.h 150 threadsafe_queue.h
151 time_zone.cpp
152 time_zone.h
151 timer.cpp 153 timer.cpp
152 timer.h 154 timer.h
153 uint128.cpp 155 uint128.cpp
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
new file mode 100644
index 000000000..ce239eb63
--- /dev/null
+++ b/src/common/time_zone.cpp
@@ -0,0 +1,49 @@
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 <chrono>
6#include <iomanip>
7#include <sstream>
8
9#include "common/logging/log.h"
10#include "common/time_zone.h"
11
12namespace Common::TimeZone {
13
14std::string GetDefaultTimeZone() {
15 return "GMT";
16}
17
18static std::string GetOsTimeZoneOffset() {
19 const std::time_t t{std::time(nullptr)};
20 const std::tm tm{*std::localtime(&t)};
21
22 std::stringstream ss;
23 ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
24
25 return ss.str();
26}
27
28static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
29 try {
30 return std::stoi(timezone);
31 } catch (const std::invalid_argument&) {
32 LOG_CRITICAL(Common, "invalid_argument with {}!", timezone);
33 return 0;
34 } catch (const std::out_of_range&) {
35 LOG_CRITICAL(Common, "out_of_range with {}!", timezone);
36 return 0;
37 }
38}
39
40std::chrono::seconds GetCurrentOffsetSeconds() {
41 const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())};
42
43 int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds
44 seconds += (offset % 100) * 60; // Convert minute component to seconds
45
46 return std::chrono::seconds{seconds};
47}
48
49} // namespace Common::TimeZone
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
new file mode 100644
index 000000000..945daa09c
--- /dev/null
+++ b/src/common/time_zone.h
@@ -0,0 +1,18 @@
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 <chrono>
8#include <string>
9
10namespace Common::TimeZone {
11
12/// Gets the default timezone, i.e. "GMT"
13std::string GetDefaultTimeZone();
14
15/// Gets the offset of the current timezone (from the default), in seconds
16std::chrono::seconds GetCurrentOffsetSeconds();
17
18} // namespace Common::TimeZone
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index f155a1341..63cd2eead 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -95,6 +95,10 @@ u32 NACP::GetSupportedLanguages() const {
95 return raw.supported_languages; 95 return raw.supported_languages;
96} 96}
97 97
98u64 NACP::GetDeviceSaveDataSize() const {
99 return raw.device_save_data_size;
100}
101
98std::vector<u8> NACP::GetRawBytes() const { 102std::vector<u8> NACP::GetRawBytes() const {
99 std::vector<u8> out(sizeof(RawNACP)); 103 std::vector<u8> out(sizeof(RawNACP));
100 std::memcpy(out.data(), &raw, sizeof(RawNACP)); 104 std::memcpy(out.data(), &raw, sizeof(RawNACP));
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 2d8c251ac..e37b2fadf 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -113,6 +113,7 @@ public:
113 u32 GetSupportedLanguages() const; 113 u32 GetSupportedLanguages() const;
114 std::vector<u8> GetRawBytes() const; 114 std::vector<u8> GetRawBytes() const;
115 bool GetUserAccountSwitchLock() const; 115 bool GetUserAccountSwitchLock() const;
116 u64 GetDeviceSaveDataSize() const;
116 117
117private: 118private:
118 RawNACP raw{}; 119 RawNACP raw{};
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index f3def93ab..adfd2c1a4 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -57,7 +57,8 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
57bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) { 57bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
58 return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage || 58 return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage ||
59 (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User 59 (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
60 desc.type == SaveDataType::SaveData && desc.title_id == 0 && desc.save_id == 0); 60 (desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) &&
61 desc.title_id == 0 && desc.save_id == 0);
61} 62}
62 63
63} // Anonymous namespace 64} // Anonymous namespace
@@ -139,8 +140,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
139 u128 user_id, u64 save_id) { 140 u128 user_id, u64 save_id) {
140 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 141 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
141 // be interpreted as the title id of the current process. 142 // be interpreted as the title id of the current process.
142 if (type == SaveDataType::SaveData && title_id == 0) { 143 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
143 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 144 if (title_id == 0) {
145 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
146 }
144 } 147 }
145 148
146 std::string out = GetSaveDataSpaceIdPath(space); 149 std::string out = GetSaveDataSpaceIdPath(space);
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index eda466a5d..9a081fbd4 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -46,7 +46,7 @@ private:
46EmuWindow::EmuWindow() { 46EmuWindow::EmuWindow() {
47 // TODO: Find a better place to set this. 47 // TODO: Find a better place to set this.
48 config.min_client_area_size = 48 config.min_client_area_size =
49 std::make_pair(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); 49 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
50 active_config = config; 50 active_config = config;
51 touch_state = std::make_shared<TouchState>(); 51 touch_state = std::make_shared<TouchState>();
52 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state); 52 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 15ecfb13d..91ecc30ab 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -8,6 +8,11 @@
8 8
9namespace Layout { 9namespace Layout {
10 10
11namespace MinimumSize {
12constexpr u32 Width = 640;
13constexpr u32 Height = 360;
14} // namespace MinimumSize
15
11namespace ScreenUndocked { 16namespace ScreenUndocked {
12constexpr u32 Width = 1280; 17constexpr u32 Width = 1280;
13constexpr u32 Height = 720; 18constexpr u32 Height = 720;
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index f6503fe2f..20c331b77 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -767,7 +767,7 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
767 {1014, nullptr, "OutputMultiProgramTagAccessLog"}, 767 {1014, nullptr, "OutputMultiProgramTagAccessLog"},
768 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 768 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
769 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, 769 {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
770 {1200, nullptr, "OpenMultiCommitManager"}, 770 {1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
771 {1300, nullptr, "OpenBisWiper"}, 771 {1300, nullptr, "OpenBisWiper"},
772 }; 772 };
773 // clang-format on 773 // clang-format on
@@ -988,4 +988,40 @@ void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
988 rb.Push(access_log_program_index); 988 rb.Push(access_log_program_index);
989} 989}
990 990
991class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
992public:
993 explicit IMultiCommitManager() : ServiceFramework("IMultiCommitManager") {
994 static const FunctionInfo functions[] = {
995 {1, &IMultiCommitManager::Add, "Add"},
996 {2, &IMultiCommitManager::Commit, "Commit"},
997 };
998 RegisterHandlers(functions);
999 }
1000
1001private:
1002 FileSys::VirtualFile backend;
1003
1004 void Add(Kernel::HLERequestContext& ctx) {
1005 LOG_WARNING(Service_FS, "(STUBBED) called");
1006
1007 IPC::ResponseBuilder rb{ctx, 2};
1008 rb.Push(RESULT_SUCCESS);
1009 }
1010
1011 void Commit(Kernel::HLERequestContext& ctx) {
1012 LOG_WARNING(Service_FS, "(STUBBED) called");
1013
1014 IPC::ResponseBuilder rb{ctx, 2};
1015 rb.Push(RESULT_SUCCESS);
1016 }
1017};
1018
1019void FSP_SRV::OpenMultiCommitManager(Kernel::HLERequestContext& ctx) {
1020 LOG_DEBUG(Service_FS, "called");
1021
1022 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1023 rb.Push(RESULT_SUCCESS);
1024 rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>());
1025}
1026
991} // namespace Service::FileSystem 1027} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index d52b55999..dfb3e395b 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -50,6 +50,7 @@ private:
50 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 50 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); 51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); 52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
53 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
53 54
54 FileSystemController& fsc; 55 FileSystemController& fsc;
55 56
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 5559587e3..c84cb1483 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -157,7 +157,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
157 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, 157 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
158 {21, &Hid::ActivateMouse, "ActivateMouse"}, 158 {21, &Hid::ActivateMouse, "ActivateMouse"},
159 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, 159 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
160 {32, nullptr, "SendKeyboardLockKeyEvent"}, 160 {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
161 {40, nullptr, "AcquireXpadIdEventHandle"}, 161 {40, nullptr, "AcquireXpadIdEventHandle"},
162 {41, nullptr, "ReleaseXpadIdEventHandle"}, 162 {41, nullptr, "ReleaseXpadIdEventHandle"},
163 {51, &Hid::ActivateXpad, "ActivateXpad"}, 163 {51, &Hid::ActivateXpad, "ActivateXpad"},
@@ -871,6 +871,15 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
871 rb.Push(RESULT_SUCCESS); 871 rb.Push(RESULT_SUCCESS);
872} 872}
873 873
874void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
875 IPC::RequestParser rp{ctx};
876 const auto flags{rp.Pop<u32>()};
877 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
878
879 IPC::ResponseBuilder rb{ctx, 2};
880 rb.Push(RESULT_SUCCESS);
881}
882
874class HidDbg final : public ServiceFramework<HidDbg> { 883class HidDbg final : public ServiceFramework<HidDbg> {
875public: 884public:
876 explicit HidDbg() : ServiceFramework{"hid:dbg"} { 885 explicit HidDbg() : ServiceFramework{"hid:dbg"} {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 23552efb1..c8ed4ad8b 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -130,6 +130,7 @@ private:
130 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 130 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
131 void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 131 void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
132 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 132 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
133 void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
133 134
134 std::shared_ptr<IAppletResource> applet_resource; 135 std::shared_ptr<IAppletResource> applet_resource;
135 Core::System& system; 136 Core::System& system;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index f1e3d832a..caca80dde 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -138,9 +138,7 @@ u32 BufferQueue::Query(QueryType type) {
138 138
139 switch (type) { 139 switch (type) {
140 case QueryType::NativeWindowFormat: 140 case QueryType::NativeWindowFormat:
141 // TODO(Subv): Use an enum for this 141 return static_cast<u32>(PixelFormat::RGBA8888);
142 static constexpr u32 FormatABGR8 = 1;
143 return FormatABGR8;
144 } 142 }
145 143
146 UNIMPLEMENTED(); 144 UNIMPLEMENTED();
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index d5f31e567..8a837e5aa 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -66,6 +66,16 @@ public:
66 Rotate270 = 0x07, 66 Rotate270 = 0x07,
67 }; 67 };
68 68
69 enum class PixelFormat : u32 {
70 RGBA8888 = 1,
71 RGBX8888 = 2,
72 RGB888 = 3,
73 RGB565 = 4,
74 BGRA8888 = 5,
75 RGBA5551 = 6,
76 RRGBA4444 = 7,
77 };
78
69 struct Buffer { 79 struct Buffer {
70 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; 80 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
71 81
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index 9d6c55865..b4dfe45e5 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -5,6 +5,7 @@
5#include <chrono> 5#include <chrono>
6#include <ctime> 6#include <ctime>
7 7
8#include "common/time_zone.h"
8#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h" 9#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
9#include "core/hle/service/time/local_system_clock_context_writer.h" 10#include "core/hle/service/time/local_system_clock_context_writer.h"
10#include "core/hle/service/time/network_system_clock_context_writer.h" 11#include "core/hle/service/time/network_system_clock_context_writer.h"
@@ -21,8 +22,16 @@ static std::chrono::seconds GetSecondsSinceEpoch() {
21 Settings::values.custom_rtc_differential; 22 Settings::values.custom_rtc_differential;
22} 23}
23 24
25static s64 GetExternalTimeZoneOffset() {
26 // With "auto" timezone setting, we use the external system's timezone offset
27 if (Settings::GetTimeZoneString() == "auto") {
28 return Common::TimeZone::GetCurrentOffsetSeconds().count();
29 }
30 return 0;
31}
32
24static s64 GetExternalRtcValue() { 33static s64 GetExternalRtcValue() {
25 return GetSecondsSinceEpoch().count(); 34 return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset();
26} 35}
27 36
28TimeManager::TimeManager(Core::System& system) 37TimeManager::TimeManager(Core::System& system)
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index 78d4acd95..c070d6e97 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -5,6 +5,7 @@
5#include <sstream> 5#include <sstream>
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/time_zone.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
@@ -14,6 +15,7 @@
14#include "core/hle/service/filesystem/filesystem.h" 15#include "core/hle/service/filesystem/filesystem.h"
15#include "core/hle/service/time/time_manager.h" 16#include "core/hle/service/time/time_manager.h"
16#include "core/hle/service/time/time_zone_content_manager.h" 17#include "core/hle/service/time/time_zone_content_manager.h"
18#include "core/settings.h"
17 19
18namespace Service::Time::TimeZone { 20namespace Service::Time::TimeZone {
19 21
@@ -68,10 +70,22 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
68 70
69TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system) 71TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
70 : system{system}, location_name_cache{BuildLocationNameCache(system)} { 72 : system{system}, location_name_cache{BuildLocationNameCache(system)} {
71 if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) { 73
74 std::string location_name;
75 const auto timezone_setting = Settings::GetTimeZoneString();
76 if (timezone_setting == "auto") {
77 location_name = Common::TimeZone::GetDefaultTimeZone();
78 } else if (timezone_setting == "default") {
79 location_name = location_name;
80 } else {
81 location_name = timezone_setting;
82 }
83
84 if (FileSys::VirtualFile vfs_file;
85 GetTimeZoneInfoFile(location_name, vfs_file) == RESULT_SUCCESS) {
72 const auto time_point{ 86 const auto time_point{
73 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; 87 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
74 time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {}, 88 time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {},
75 vfs_file); 89 vfs_file);
76 } else { 90 } else {
77 time_zone_manager.MarkAsInitialized(); 91 time_zone_manager.MarkAsInitialized();
@@ -114,6 +128,12 @@ ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& locati
114 128
115 vfs_file = zoneinfo_dir->GetFile(location_name); 129 vfs_file = zoneinfo_dir->GetFile(location_name);
116 if (!vfs_file) { 130 if (!vfs_file) {
131 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
132 time_zone_binary_titleid, location_name);
133 vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
134 }
135
136 if (!vfs_file) {
117 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid, 137 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
118 location_name); 138 location_name);
119 return ERROR_TIME_NOT_FOUND; 139 return ERROR_TIME_NOT_FOUND;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 2b0bdc4d3..da53cde05 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -63,6 +63,21 @@ const std::array<const char*, NumMouseButtons> mapping = {{
63 63
64Values values = {}; 64Values values = {};
65 65
66std::string GetTimeZoneString() {
67 static constexpr std::array<const char*, 46> timezones{{
68 "auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
69 "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
70 "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
71 "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
72 "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
73 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
74 }};
75
76 ASSERT(Settings::values.time_zone_index < timezones.size());
77
78 return timezones[Settings::values.time_zone_index];
79}
80
66void Apply() { 81void Apply() {
67 GDBStub::SetServerPort(values.gdbstub_port); 82 GDBStub::SetServerPort(values.gdbstub_port);
68 GDBStub::ToggleServer(values.use_gdbstub); 83 GDBStub::ToggleServer(values.use_gdbstub);
@@ -87,6 +102,7 @@ void LogSettings() {
87 LogSetting("System_CurrentUser", Settings::values.current_user); 102 LogSetting("System_CurrentUser", Settings::values.current_user);
88 LogSetting("System_LanguageIndex", Settings::values.language_index); 103 LogSetting("System_LanguageIndex", Settings::values.language_index);
89 LogSetting("System_RegionIndex", Settings::values.region_index); 104 LogSetting("System_RegionIndex", Settings::values.region_index);
105 LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
90 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); 106 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
91 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 107 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
92 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 108 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
diff --git a/src/core/settings.h b/src/core/settings.h
index 163900f0b..c1266b341 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -394,6 +394,7 @@ struct Values {
394 s32 current_user; 394 s32 current_user;
395 s32 language_index; 395 s32 language_index;
396 s32 region_index; 396 s32 region_index;
397 s32 time_zone_index;
397 s32 sound_index; 398 s32 sound_index;
398 399
399 // Controls 400 // Controls
@@ -490,6 +491,9 @@ struct Values {
490bool IsGPULevelExtreme(); 491bool IsGPULevelExtreme();
491bool IsGPULevelHigh(); 492bool IsGPULevelHigh();
492 493
494std::string GetTimeZoneString();
495
493void Apply(); 496void Apply();
494void LogSettings(); 497void LogSettings();
498
495} // namespace Settings 499} // namespace Settings
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index bdc023d54..f2f96ac33 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -54,9 +54,7 @@ bool DmaPusher::Step() {
54 return true; 54 return true;
55 }); 55 });
56 const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]}; 56 const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
57 GPUVAddr dma_get = command_list_header.addr; 57 const GPUVAddr dma_get = command_list_header.addr;
58 GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
59 bool non_main = command_list_header.is_non_main;
60 58
61 if (dma_pushbuffer_subindex >= command_list.size()) { 59 if (dma_pushbuffer_subindex >= command_list.size()) {
62 // We've gone through the current list, remove it from the queue 60 // We've gone through the current list, remove it from the queue
@@ -133,11 +131,6 @@ bool DmaPusher::Step() {
133 index++; 131 index++;
134 } 132 }
135 133
136 if (!non_main) {
137 // TODO (degasus): This is dead code, as dma_mget is never read.
138 dma_mget = dma_put;
139 }
140
141 return true; 134 return true;
142} 135}
143 136
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index e8b714e94..efa90d170 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -102,7 +102,6 @@ private:
102 DmaState dma_state{}; 102 DmaState dma_state{};
103 bool dma_increment_once{}; 103 bool dma_increment_once{};
104 104
105 GPUVAddr dma_mget{}; ///< main pushbuffer last read address
106 bool ib_enable{true}; ///< IB mode enabled 105 bool ib_enable{true}; ///< IB mode enabled
107 106
108 std::array<Tegra::Engines::EngineInterface*, max_subchannels> subchannels{}; 107 std::array<Tegra::Engines::EngineInterface*, max_subchannels> subchannels{};
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 8dae754d4..e7cb87589 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -168,18 +168,22 @@ enum class Pred : u64 {
168}; 168};
169 169
170enum class PredCondition : u64 { 170enum class PredCondition : u64 {
171 LessThan = 1, 171 F = 0, // Always false
172 Equal = 2, 172 LT = 1, // Ordered less than
173 LessEqual = 3, 173 EQ = 2, // Ordered equal
174 GreaterThan = 4, 174 LE = 3, // Ordered less than or equal
175 NotEqual = 5, 175 GT = 4, // Ordered greater than
176 GreaterEqual = 6, 176 NE = 5, // Ordered not equal
177 LessThanWithNan = 9, 177 GE = 6, // Ordered greater than or equal
178 LessEqualWithNan = 11, 178 NUM = 7, // Ordered
179 GreaterThanWithNan = 12, 179 NAN_ = 8, // Unordered
180 NotEqualWithNan = 13, 180 LTU = 9, // Unordered less than
181 GreaterEqualWithNan = 14, 181 EQU = 10, // Unordered equal
182 // TODO(Subv): Other condition types 182 LEU = 11, // Unordered less than or equal
183 GTU = 12, // Unordered greater than
184 NEU = 13, // Unordered not equal
185 GEU = 14, // Unordered greater than or equal
186 T = 15, // Always true
183}; 187};
184 188
185enum class PredOperation : u64 { 189enum class PredOperation : u64 {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 99fd4ae2c..960ebf1a1 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1840,34 +1840,40 @@ private:
1840 Type::HalfFloat}; 1840 Type::HalfFloat};
1841 } 1841 }
1842 1842
1843 template <Type type> 1843 template <const std::string_view& op, Type type, bool unordered = false>
1844 Expression LogicalLessThan(Operation operation) { 1844 Expression Comparison(Operation operation) {
1845 return GenerateBinaryInfix(operation, "<", Type::Bool, type, type); 1845 static_assert(!unordered || type == Type::Float);
1846 } 1846
1847 1847 const Expression expr = GenerateBinaryInfix(operation, op, Type::Bool, type, type);
1848 template <Type type> 1848
1849 Expression LogicalEqual(Operation operation) { 1849 if constexpr (op.compare("!=") == 0 && type == Type::Float && !unordered) {
1850 return GenerateBinaryInfix(operation, "==", Type::Bool, type, type); 1850 // GLSL's operator!=(float, float) doesn't seem be ordered. This happens on both AMD's
1851 } 1851 // and Nvidia's proprietary stacks. Manually force an ordered comparison.
1852 1852 return {fmt::format("({} && !isnan({}) && !isnan({}))", expr.AsBool(),
1853 template <Type type> 1853 VisitOperand(operation, 0).AsFloat(),
1854 Expression LogicalLessEqual(Operation operation) { 1854 VisitOperand(operation, 1).AsFloat()),
1855 return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type); 1855 Type::Bool};
1856 } 1856 }
1857 1857 if constexpr (!unordered) {
1858 template <Type type> 1858 return expr;
1859 Expression LogicalGreaterThan(Operation operation) { 1859 }
1860 return GenerateBinaryInfix(operation, ">", Type::Bool, type, type); 1860 // Unordered comparisons are always true for NaN operands.
1861 return {fmt::format("({} || isnan({}) || isnan({}))", expr.AsBool(),
1862 VisitOperand(operation, 0).AsFloat(),
1863 VisitOperand(operation, 1).AsFloat()),
1864 Type::Bool};
1861 } 1865 }
1862 1866
1863 template <Type type> 1867 Expression FOrdered(Operation operation) {
1864 Expression LogicalNotEqual(Operation operation) { 1868 return {fmt::format("(!isnan({}) && !isnan({}))", VisitOperand(operation, 0).AsFloat(),
1865 return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type); 1869 VisitOperand(operation, 1).AsFloat()),
1870 Type::Bool};
1866 } 1871 }
1867 1872
1868 template <Type type> 1873 Expression FUnordered(Operation operation) {
1869 Expression LogicalGreaterEqual(Operation operation) { 1874 return {fmt::format("(isnan({}) || isnan({}))", VisitOperand(operation, 0).AsFloat(),
1870 return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type); 1875 VisitOperand(operation, 1).AsFloat()),
1876 Type::Bool};
1871 } 1877 }
1872 1878
1873 Expression LogicalAddCarry(Operation operation) { 1879 Expression LogicalAddCarry(Operation operation) {
@@ -2324,6 +2330,13 @@ private:
2324 Func() = delete; 2330 Func() = delete;
2325 ~Func() = delete; 2331 ~Func() = delete;
2326 2332
2333 static constexpr std::string_view LessThan = "<";
2334 static constexpr std::string_view Equal = "==";
2335 static constexpr std::string_view LessEqual = "<=";
2336 static constexpr std::string_view GreaterThan = ">";
2337 static constexpr std::string_view NotEqual = "!=";
2338 static constexpr std::string_view GreaterEqual = ">=";
2339
2327 static constexpr std::string_view Add = "Add"; 2340 static constexpr std::string_view Add = "Add";
2328 static constexpr std::string_view Min = "Min"; 2341 static constexpr std::string_view Min = "Min";
2329 static constexpr std::string_view Max = "Max"; 2342 static constexpr std::string_view Max = "Max";
@@ -2425,27 +2438,34 @@ private:
2425 &GLSLDecompiler::LogicalPick2, 2438 &GLSLDecompiler::LogicalPick2,
2426 &GLSLDecompiler::LogicalAnd2, 2439 &GLSLDecompiler::LogicalAnd2,
2427 2440
2428 &GLSLDecompiler::LogicalLessThan<Type::Float>, 2441 &GLSLDecompiler::Comparison<Func::LessThan, Type::Float, false>,
2429 &GLSLDecompiler::LogicalEqual<Type::Float>, 2442 &GLSLDecompiler::Comparison<Func::Equal, Type::Float, false>,
2430 &GLSLDecompiler::LogicalLessEqual<Type::Float>, 2443 &GLSLDecompiler::Comparison<Func::LessEqual, Type::Float, false>,
2431 &GLSLDecompiler::LogicalGreaterThan<Type::Float>, 2444 &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Float, false>,
2432 &GLSLDecompiler::LogicalNotEqual<Type::Float>, 2445 &GLSLDecompiler::Comparison<Func::NotEqual, Type::Float, false>,
2433 &GLSLDecompiler::LogicalGreaterEqual<Type::Float>, 2446 &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Float, false>,
2434 &GLSLDecompiler::LogicalFIsNan, 2447 &GLSLDecompiler::FOrdered,
2435 2448 &GLSLDecompiler::FUnordered,
2436 &GLSLDecompiler::LogicalLessThan<Type::Int>, 2449 &GLSLDecompiler::Comparison<Func::LessThan, Type::Float, true>,
2437 &GLSLDecompiler::LogicalEqual<Type::Int>, 2450 &GLSLDecompiler::Comparison<Func::Equal, Type::Float, true>,
2438 &GLSLDecompiler::LogicalLessEqual<Type::Int>, 2451 &GLSLDecompiler::Comparison<Func::LessEqual, Type::Float, true>,
2439 &GLSLDecompiler::LogicalGreaterThan<Type::Int>, 2452 &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Float, true>,
2440 &GLSLDecompiler::LogicalNotEqual<Type::Int>, 2453 &GLSLDecompiler::Comparison<Func::NotEqual, Type::Float, true>,
2441 &GLSLDecompiler::LogicalGreaterEqual<Type::Int>, 2454 &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Float, true>,
2442 2455
2443 &GLSLDecompiler::LogicalLessThan<Type::Uint>, 2456 &GLSLDecompiler::Comparison<Func::LessThan, Type::Int>,
2444 &GLSLDecompiler::LogicalEqual<Type::Uint>, 2457 &GLSLDecompiler::Comparison<Func::Equal, Type::Int>,
2445 &GLSLDecompiler::LogicalLessEqual<Type::Uint>, 2458 &GLSLDecompiler::Comparison<Func::LessEqual, Type::Int>,
2446 &GLSLDecompiler::LogicalGreaterThan<Type::Uint>, 2459 &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Int>,
2447 &GLSLDecompiler::LogicalNotEqual<Type::Uint>, 2460 &GLSLDecompiler::Comparison<Func::NotEqual, Type::Int>,
2448 &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, 2461 &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Int>,
2462
2463 &GLSLDecompiler::Comparison<Func::LessThan, Type::Uint>,
2464 &GLSLDecompiler::Comparison<Func::Equal, Type::Uint>,
2465 &GLSLDecompiler::Comparison<Func::LessEqual, Type::Uint>,
2466 &GLSLDecompiler::Comparison<Func::GreaterThan, Type::Uint>,
2467 &GLSLDecompiler::Comparison<Func::NotEqual, Type::Uint>,
2468 &GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Uint>,
2449 2469
2450 &GLSLDecompiler::LogicalAddCarry, 2470 &GLSLDecompiler::LogicalAddCarry,
2451 2471
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 6cead3a28..568744e3c 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -93,6 +93,7 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
93 tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); 93 tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
94 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 94 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
95 logic_op.Assign(PackLogicOp(regs.logic_op.operation)); 95 logic_op.Assign(PackLogicOp(regs.logic_op.operation));
96 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
96 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast 97 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
97} 98}
98 99
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index cecaee48d..31a6398f2 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -164,6 +164,7 @@ struct FixedPipelineState {
164 BitField<23, 1, u32> tessellation_clockwise; 164 BitField<23, 1, u32> tessellation_clockwise;
165 BitField<24, 1, u32> logic_op_enable; 165 BitField<24, 1, u32> logic_op_enable;
166 BitField<25, 4, u32> logic_op; 166 BitField<25, 4, u32> logic_op;
167 BitField<29, 1, u32> rasterize_enable;
167 }; 168 };
168 169
169 // TODO(Rodrigo): Move this to push constants 170 // TODO(Rodrigo): Move this to push constants
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 5beea6a03..69b6bba00 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -281,7 +281,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
281 rasterization_ci.pNext = nullptr; 281 rasterization_ci.pNext = nullptr;
282 rasterization_ci.flags = 0; 282 rasterization_ci.flags = 0;
283 rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; 283 rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
284 rasterization_ci.rasterizerDiscardEnable = VK_FALSE; 284 rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
285 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; 285 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
286 rasterization_ci.cullMode = 286 rasterization_ci.cullMode =
287 rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; 287 rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 8b009fc22..17a2efe8e 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -569,7 +569,9 @@ void RasterizerVulkan::ReleaseFences() {
569} 569}
570 570
571void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { 571void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
572 FlushRegion(addr, size); 572 if (Settings::IsGPULevelExtreme()) {
573 FlushRegion(addr, size);
574 }
573 InvalidateRegion(addr, size); 575 InvalidateRegion(addr, size);
574} 576}
575 577
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 18678968c..167e20e91 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -1618,6 +1618,24 @@ private:
1618 return {}; 1618 return {};
1619 } 1619 }
1620 1620
1621 Expression LogicalFOrdered(Operation operation) {
1622 // Emulate SPIR-V's OpOrdered
1623 const Id op_a = AsFloat(Visit(operation[0]));
1624 const Id op_b = AsFloat(Visit(operation[1]));
1625 const Id is_num_a = OpFOrdEqual(t_bool, op_a, op_a);
1626 const Id is_num_b = OpFOrdEqual(t_bool, op_b, op_b);
1627 return {OpLogicalAnd(t_bool, is_num_a, is_num_b), Type::Bool};
1628 }
1629
1630 Expression LogicalFUnordered(Operation operation) {
1631 // Emulate SPIR-V's OpUnordered
1632 const Id op_a = AsFloat(Visit(operation[0]));
1633 const Id op_b = AsFloat(Visit(operation[1]));
1634 const Id is_nan_a = OpIsNan(t_bool, op_a);
1635 const Id is_nan_b = OpIsNan(t_bool, op_b);
1636 return {OpLogicalOr(t_bool, is_nan_a, is_nan_b), Type::Bool};
1637 }
1638
1621 Id GetTextureSampler(Operation operation) { 1639 Id GetTextureSampler(Operation operation) {
1622 const auto& meta = std::get<MetaTexture>(operation.GetMeta()); 1640 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1623 ASSERT(!meta.sampler.is_buffer); 1641 ASSERT(!meta.sampler.is_buffer);
@@ -2511,7 +2529,14 @@ private:
2511 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>, 2529 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>,
2512 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>, 2530 &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>,
2513 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>, 2531 &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>,
2514 &SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool, Type::Float>, 2532 &SPIRVDecompiler::LogicalFOrdered,
2533 &SPIRVDecompiler::LogicalFUnordered,
2534 &SPIRVDecompiler::Binary<&Module::OpFUnordLessThan, Type::Bool, Type::Float>,
2535 &SPIRVDecompiler::Binary<&Module::OpFUnordEqual, Type::Bool, Type::Float>,
2536 &SPIRVDecompiler::Binary<&Module::OpFUnordLessThanEqual, Type::Bool, Type::Float>,
2537 &SPIRVDecompiler::Binary<&Module::OpFUnordGreaterThan, Type::Bool, Type::Float>,
2538 &SPIRVDecompiler::Binary<&Module::OpFUnordNotEqual, Type::Bool, Type::Float>,
2539 &SPIRVDecompiler::Binary<&Module::OpFUnordGreaterThanEqual, Type::Bool, Type::Float>,
2515 2540
2516 &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>, 2541 &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>,
2517 &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>, 2542 &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>,
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index a75a5cc63..eeac328a6 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -255,7 +255,7 @@ void ShaderIR::InsertControlFlow(NodeBlock& bb, const ShaderBlock& block) {
255 Node n = Operation(OperationCode::Branch, Immediate(branch_case.address)); 255 Node n = Operation(OperationCode::Branch, Immediate(branch_case.address));
256 Node op_b = Immediate(branch_case.cmp_value); 256 Node op_b = Immediate(branch_case.cmp_value);
257 Node condition = 257 Node condition =
258 GetPredicateComparisonInteger(Tegra::Shader::PredCondition::Equal, false, op_a, op_b); 258 GetPredicateComparisonInteger(Tegra::Shader::PredCondition::EQ, false, op_a, op_b);
259 auto result = Conditional(condition, {n}); 259 auto result = Conditional(condition, {n});
260 bb.push_back(result); 260 bb.push_back(result);
261 global_code.push_back(result); 261 global_code.push_back(result);
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp
index 6191ffba1..c83dc6615 100644
--- a/src/video_core/shader/decode/xmad.cpp
+++ b/src/video_core/shader/decode/xmad.cpp
@@ -97,19 +97,19 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
97 return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b); 97 return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b);
98 } 98 }
99 case Tegra::Shader::XmadMode::CSfu: { 99 case Tegra::Shader::XmadMode::CSfu: {
100 const Node comp_a = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_a, 100 const Node comp_a =
101 op_a, Immediate(0)); 101 GetPredicateComparisonInteger(PredCondition::EQ, is_signed_a, op_a, Immediate(0));
102 const Node comp_b = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_b, 102 const Node comp_b =
103 op_b, Immediate(0)); 103 GetPredicateComparisonInteger(PredCondition::EQ, is_signed_b, op_b, Immediate(0));
104 const Node comp = Operation(OperationCode::LogicalOr, comp_a, comp_b); 104 const Node comp = Operation(OperationCode::LogicalOr, comp_a, comp_b);
105 105
106 const Node comp_minus_a = GetPredicateComparisonInteger( 106 const Node comp_minus_a = GetPredicateComparisonInteger(
107 PredCondition::NotEqual, is_signed_a, 107 PredCondition::NE, is_signed_a,
108 SignedOperation(OperationCode::IBitwiseAnd, is_signed_a, op_a, 108 SignedOperation(OperationCode::IBitwiseAnd, is_signed_a, op_a,
109 Immediate(0x80000000)), 109 Immediate(0x80000000)),
110 Immediate(0)); 110 Immediate(0));
111 const Node comp_minus_b = GetPredicateComparisonInteger( 111 const Node comp_minus_b = GetPredicateComparisonInteger(
112 PredCondition::NotEqual, is_signed_b, 112 PredCondition::NE, is_signed_b,
113 SignedOperation(OperationCode::IBitwiseAnd, is_signed_b, op_b, 113 SignedOperation(OperationCode::IBitwiseAnd, is_signed_b, op_b,
114 Immediate(0x80000000)), 114 Immediate(0x80000000)),
115 Immediate(0)); 115 Immediate(0));
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index 601c822d2..f75b62240 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -110,13 +110,20 @@ enum class OperationCode {
110 LogicalPick2, /// (bool2 pair, uint index) -> bool 110 LogicalPick2, /// (bool2 pair, uint index) -> bool
111 LogicalAnd2, /// (bool2 a) -> bool 111 LogicalAnd2, /// (bool2 a) -> bool
112 112
113 LogicalFLessThan, /// (float a, float b) -> bool 113 LogicalFOrdLessThan, /// (float a, float b) -> bool
114 LogicalFEqual, /// (float a, float b) -> bool 114 LogicalFOrdEqual, /// (float a, float b) -> bool
115 LogicalFLessEqual, /// (float a, float b) -> bool 115 LogicalFOrdLessEqual, /// (float a, float b) -> bool
116 LogicalFGreaterThan, /// (float a, float b) -> bool 116 LogicalFOrdGreaterThan, /// (float a, float b) -> bool
117 LogicalFNotEqual, /// (float a, float b) -> bool 117 LogicalFOrdNotEqual, /// (float a, float b) -> bool
118 LogicalFGreaterEqual, /// (float a, float b) -> bool 118 LogicalFOrdGreaterEqual, /// (float a, float b) -> bool
119 LogicalFIsNan, /// (float a) -> bool 119 LogicalFOrdered, /// (float a, float b) -> bool
120 LogicalFUnordered, /// (float a, float b) -> bool
121 LogicalFUnordLessThan, /// (float a, float b) -> bool
122 LogicalFUnordEqual, /// (float a, float b) -> bool
123 LogicalFUnordLessEqual, /// (float a, float b) -> bool
124 LogicalFUnordGreaterThan, /// (float a, float b) -> bool
125 LogicalFUnordNotEqual, /// (float a, float b) -> bool
126 LogicalFUnordGreaterEqual, /// (float a, float b) -> bool
120 127
121 LogicalILessThan, /// (int a, int b) -> bool 128 LogicalILessThan, /// (int a, int b) -> bool
122 LogicalIEqual, /// (int a, int b) -> bool 129 LogicalIEqual, /// (int a, int b) -> bool
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 822674926..e322c3402 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -10,6 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "video_core/engines/shader_bytecode.h" 12#include "video_core/engines/shader_bytecode.h"
13#include "video_core/shader/node.h"
13#include "video_core/shader/node_helper.h" 14#include "video_core/shader/node_helper.h"
14#include "video_core/shader/registry.h" 15#include "video_core/shader/registry.h"
15#include "video_core/shader/shader_ir.h" 16#include "video_core/shader/shader_ir.h"
@@ -243,56 +244,44 @@ Node ShaderIR::GetSaturatedHalfFloat(Node value, bool saturate) {
243} 244}
244 245
245Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) { 246Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) {
247 if (condition == PredCondition::T) {
248 return GetPredicate(true);
249 } else if (condition == PredCondition::F) {
250 return GetPredicate(false);
251 }
252
246 static constexpr std::array comparison_table{ 253 static constexpr std::array comparison_table{
247 std::pair{PredCondition::LessThan, OperationCode::LogicalFLessThan}, 254 OperationCode(0),
248 std::pair{PredCondition::Equal, OperationCode::LogicalFEqual}, 255 OperationCode::LogicalFOrdLessThan, // LT
249 std::pair{PredCondition::LessEqual, OperationCode::LogicalFLessEqual}, 256 OperationCode::LogicalFOrdEqual, // EQ
250 std::pair{PredCondition::GreaterThan, OperationCode::LogicalFGreaterThan}, 257 OperationCode::LogicalFOrdLessEqual, // LE
251 std::pair{PredCondition::NotEqual, OperationCode::LogicalFNotEqual}, 258 OperationCode::LogicalFOrdGreaterThan, // GT
252 std::pair{PredCondition::GreaterEqual, OperationCode::LogicalFGreaterEqual}, 259 OperationCode::LogicalFOrdNotEqual, // NE
253 std::pair{PredCondition::LessThanWithNan, OperationCode::LogicalFLessThan}, 260 OperationCode::LogicalFOrdGreaterEqual, // GE
254 std::pair{PredCondition::NotEqualWithNan, OperationCode::LogicalFNotEqual}, 261 OperationCode::LogicalFOrdered, // NUM
255 std::pair{PredCondition::LessEqualWithNan, OperationCode::LogicalFLessEqual}, 262 OperationCode::LogicalFUnordered, // NAN
256 std::pair{PredCondition::GreaterThanWithNan, OperationCode::LogicalFGreaterThan}, 263 OperationCode::LogicalFUnordLessThan, // LTU
257 std::pair{PredCondition::GreaterEqualWithNan, OperationCode::LogicalFGreaterEqual}, 264 OperationCode::LogicalFUnordEqual, // EQU
265 OperationCode::LogicalFUnordLessEqual, // LEU
266 OperationCode::LogicalFUnordGreaterThan, // GTU
267 OperationCode::LogicalFUnordNotEqual, // NEU
268 OperationCode::LogicalFUnordGreaterEqual, // GEU
258 }; 269 };
270 const std::size_t index = static_cast<std::size_t>(condition);
271 ASSERT_MSG(index < std::size(comparison_table), "Invalid condition={}", index);
259 272
260 const auto comparison = 273 return Operation(comparison_table[index], op_a, op_b);
261 std::find_if(comparison_table.cbegin(), comparison_table.cend(),
262 [condition](const auto entry) { return condition == entry.first; });
263 UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(),
264 "Unknown predicate comparison operation");
265
266 Node predicate = Operation(comparison->second, NO_PRECISE, op_a, op_b);
267
268 if (condition == PredCondition::LessThanWithNan ||
269 condition == PredCondition::NotEqualWithNan ||
270 condition == PredCondition::LessEqualWithNan ||
271 condition == PredCondition::GreaterThanWithNan ||
272 condition == PredCondition::GreaterEqualWithNan) {
273 predicate = Operation(OperationCode::LogicalOr, predicate,
274 Operation(OperationCode::LogicalFIsNan, op_a));
275 predicate = Operation(OperationCode::LogicalOr, predicate,
276 Operation(OperationCode::LogicalFIsNan, op_b));
277 }
278
279 return predicate;
280} 274}
281 275
282Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a, 276Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a,
283 Node op_b) { 277 Node op_b) {
284 static constexpr std::array comparison_table{ 278 static constexpr std::array comparison_table{
285 std::pair{PredCondition::LessThan, OperationCode::LogicalILessThan}, 279 std::pair{PredCondition::LT, OperationCode::LogicalILessThan},
286 std::pair{PredCondition::Equal, OperationCode::LogicalIEqual}, 280 std::pair{PredCondition::EQ, OperationCode::LogicalIEqual},
287 std::pair{PredCondition::LessEqual, OperationCode::LogicalILessEqual}, 281 std::pair{PredCondition::LE, OperationCode::LogicalILessEqual},
288 std::pair{PredCondition::GreaterThan, OperationCode::LogicalIGreaterThan}, 282 std::pair{PredCondition::GT, OperationCode::LogicalIGreaterThan},
289 std::pair{PredCondition::NotEqual, OperationCode::LogicalINotEqual}, 283 std::pair{PredCondition::NE, OperationCode::LogicalINotEqual},
290 std::pair{PredCondition::GreaterEqual, OperationCode::LogicalIGreaterEqual}, 284 std::pair{PredCondition::GE, OperationCode::LogicalIGreaterEqual},
291 std::pair{PredCondition::LessThanWithNan, OperationCode::LogicalILessThan},
292 std::pair{PredCondition::NotEqualWithNan, OperationCode::LogicalINotEqual},
293 std::pair{PredCondition::LessEqualWithNan, OperationCode::LogicalILessEqual},
294 std::pair{PredCondition::GreaterThanWithNan, OperationCode::LogicalIGreaterThan},
295 std::pair{PredCondition::GreaterEqualWithNan, OperationCode::LogicalIGreaterEqual},
296 }; 285 };
297 286
298 const auto comparison = 287 const auto comparison =
@@ -301,32 +290,24 @@ Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_si
301 UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(), 290 UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(),
302 "Unknown predicate comparison operation"); 291 "Unknown predicate comparison operation");
303 292
304 Node predicate = SignedOperation(comparison->second, is_signed, NO_PRECISE, std::move(op_a), 293 return SignedOperation(comparison->second, is_signed, NO_PRECISE, std::move(op_a),
305 std::move(op_b)); 294 std::move(op_b));
306
307 UNIMPLEMENTED_IF_MSG(condition == PredCondition::LessThanWithNan ||
308 condition == PredCondition::NotEqualWithNan ||
309 condition == PredCondition::LessEqualWithNan ||
310 condition == PredCondition::GreaterThanWithNan ||
311 condition == PredCondition::GreaterEqualWithNan,
312 "NaN comparisons for integers are not implemented");
313 return predicate;
314} 295}
315 296
316Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a, 297Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a,
317 Node op_b) { 298 Node op_b) {
318 static constexpr std::array comparison_table{ 299 static constexpr std::array comparison_table{
319 std::pair{PredCondition::LessThan, OperationCode::Logical2HLessThan}, 300 std::pair{PredCondition::LT, OperationCode::Logical2HLessThan},
320 std::pair{PredCondition::Equal, OperationCode::Logical2HEqual}, 301 std::pair{PredCondition::EQ, OperationCode::Logical2HEqual},
321 std::pair{PredCondition::LessEqual, OperationCode::Logical2HLessEqual}, 302 std::pair{PredCondition::LE, OperationCode::Logical2HLessEqual},
322 std::pair{PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan}, 303 std::pair{PredCondition::GT, OperationCode::Logical2HGreaterThan},
323 std::pair{PredCondition::NotEqual, OperationCode::Logical2HNotEqual}, 304 std::pair{PredCondition::NE, OperationCode::Logical2HNotEqual},
324 std::pair{PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual}, 305 std::pair{PredCondition::GE, OperationCode::Logical2HGreaterEqual},
325 std::pair{PredCondition::LessThanWithNan, OperationCode::Logical2HLessThanWithNan}, 306 std::pair{PredCondition::LTU, OperationCode::Logical2HLessThanWithNan},
326 std::pair{PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqualWithNan}, 307 std::pair{PredCondition::LEU, OperationCode::Logical2HLessEqualWithNan},
327 std::pair{PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqualWithNan}, 308 std::pair{PredCondition::GTU, OperationCode::Logical2HGreaterThanWithNan},
328 std::pair{PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThanWithNan}, 309 std::pair{PredCondition::NEU, OperationCode::Logical2HNotEqualWithNan},
329 std::pair{PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqualWithNan}, 310 std::pair{PredCondition::GEU, OperationCode::Logical2HGreaterEqualWithNan},
330 }; 311 };
331 312
332 const auto comparison = 313 const auto comparison =
@@ -397,7 +378,7 @@ void ShaderIR::SetInternalFlagsFromFloat(NodeBlock& bb, Node value, bool sets_cc
397 if (!sets_cc) { 378 if (!sets_cc) {
398 return; 379 return;
399 } 380 }
400 Node zerop = Operation(OperationCode::LogicalFEqual, std::move(value), Immediate(0.0f)); 381 Node zerop = Operation(OperationCode::LogicalFOrdEqual, std::move(value), Immediate(0.0f));
401 SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); 382 SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
402 LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete"); 383 LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete");
403} 384}
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 3d759f77b..1adf8932b 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -150,18 +150,19 @@ public:
150 } 150 }
151 151
152 void MakeCurrent() override { 152 void MakeCurrent() override {
153 if (is_current) { 153 // We can't track the current state of the underlying context in this wrapper class because
154 return; 154 // Qt may make the underlying context not current for one reason or another. In particular,
155 // the WebBrowser uses GL, so it seems to conflict if we aren't careful.
156 // Instead of always just making the context current (which does not have any caching to
157 // check if the underlying context is already current) we can check for the current context
158 // in the thread local data by calling `currentContext()` and checking if its ours.
159 if (QOpenGLContext::currentContext() != context.get()) {
160 context->makeCurrent(surface);
155 } 161 }
156 is_current = context->makeCurrent(surface);
157 } 162 }
158 163
159 void DoneCurrent() override { 164 void DoneCurrent() override {
160 if (!is_current) {
161 return;
162 }
163 context->doneCurrent(); 165 context->doneCurrent();
164 is_current = false;
165 } 166 }
166 167
167 QOpenGLContext* GetShareContext() { 168 QOpenGLContext* GetShareContext() {
@@ -178,7 +179,6 @@ private:
178 std::unique_ptr<QOpenGLContext> context; 179 std::unique_ptr<QOpenGLContext> context;
179 std::unique_ptr<QOffscreenSurface> offscreen_surface{}; 180 std::unique_ptr<QOffscreenSurface> offscreen_surface{};
180 QSurface* surface; 181 QSurface* surface;
181 bool is_current = false;
182}; 182};
183 183
184class DummyContext : public Core::Frontend::GraphicsContext {}; 184class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 75c6cf20b..27775701d 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -687,6 +687,8 @@ void Config::ReadSystemValues() {
687 687
688 Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt(); 688 Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt();
689 689
690 Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt();
691
690 const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); 692 const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
691 if (rng_seed_enabled) { 693 if (rng_seed_enabled) {
692 Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); 694 Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
@@ -1126,6 +1128,7 @@ void Config::SaveSystemValues() {
1126 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); 1128 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
1127 WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); 1129 WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
1128 WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1); 1130 WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1);
1131 WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
1129 1132
1130 WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); 1133 WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
1131 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); 1134 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index f49cd4c8f..10315e7a6 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -57,6 +57,7 @@ void ConfigureSystem::SetConfiguration() {
57 57
58 ui->combo_language->setCurrentIndex(Settings::values.language_index); 58 ui->combo_language->setCurrentIndex(Settings::values.language_index);
59 ui->combo_region->setCurrentIndex(Settings::values.region_index); 59 ui->combo_region->setCurrentIndex(Settings::values.region_index);
60 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index);
60 ui->combo_sound->setCurrentIndex(Settings::values.sound_index); 61 ui->combo_sound->setCurrentIndex(Settings::values.sound_index);
61 62
62 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); 63 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
@@ -84,6 +85,7 @@ void ConfigureSystem::ApplyConfiguration() {
84 85
85 Settings::values.language_index = ui->combo_language->currentIndex(); 86 Settings::values.language_index = ui->combo_language->currentIndex();
86 Settings::values.region_index = ui->combo_region->currentIndex(); 87 Settings::values.region_index = ui->combo_region->currentIndex();
88 Settings::values.time_zone_index = ui->combo_time_zone->currentIndex();
87 Settings::values.sound_index = ui->combo_sound->currentIndex(); 89 Settings::values.sound_index = ui->combo_sound->currentIndex();
88 90
89 if (ui->rng_seed_checkbox->isChecked()) { 91 if (ui->rng_seed_checkbox->isChecked()) {
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index d8fa2d2cc..26d42d5c5 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -37,5 +37,6 @@ private:
37 37
38 int language_index = 0; 38 int language_index = 0;
39 int region_index = 0; 39 int region_index = 0;
40 int time_zone_index = 0;
40 int sound_index = 0; 41 int sound_index = 0;
41}; 42};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 4e2c7e76e..9c8cca6dc 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -22,14 +22,14 @@
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QGridLayout" name="gridLayout">
25 <item row="2" column="0"> 25 <item row="3" column="0">
26 <widget class="QLabel" name="label_sound"> 26 <widget class="QLabel" name="label_sound">
27 <property name="text"> 27 <property name="text">
28 <string>Sound output mode</string> 28 <string>Sound output mode</string>
29 </property> 29 </property>
30 </widget> 30 </widget>
31 </item> 31 </item>
32 <item row="3" column="0"> 32 <item row="4" column="0">
33 <widget class="QLabel" name="label_console_id"> 33 <widget class="QLabel" name="label_console_id">
34 <property name="text"> 34 <property name="text">
35 <string>Console ID:</string> 35 <string>Console ID:</string>
@@ -174,14 +174,255 @@
174 </item> 174 </item>
175 </widget> 175 </widget>
176 </item> 176 </item>
177 <item row="5" column="0"> 177 <item row="2" column="0">
178 <widget class="QLabel" name="label_timezone">
179 <property name="text">
180 <string>Time Zone:</string>
181 </property>
182 </widget>
183 </item>
184 <item row="2" column="1">
185 <widget class="QComboBox" name="combo_time_zone">
186 <item>
187 <property name="text">
188 <string>Auto</string>
189 </property>
190 </item>
191 <item>
192 <property name="text">
193 <string>Default</string>
194 </property>
195 </item>
196 <item>
197 <property name="text">
198 <string>CET</string>
199 </property>
200 </item>
201 <item>
202 <property name="text">
203 <string>CST6CDT</string>
204 </property>
205 </item>
206 <item>
207 <property name="text">
208 <string>Cuba</string>
209 </property>
210 </item>
211 <item>
212 <property name="text">
213 <string>EET</string>
214 </property>
215 </item>
216 <item>
217 <property name="text">
218 <string>Egypt</string>
219 </property>
220 </item>
221 <item>
222 <property name="text">
223 <string>Eire</string>
224 </property>
225 </item>
226 <item>
227 <property name="text">
228 <string>EST</string>
229 </property>
230 </item>
231 <item>
232 <property name="text">
233 <string>EST5EDT</string>
234 </property>
235 </item>
236 <item>
237 <property name="text">
238 <string>GB</string>
239 </property>
240 </item>
241 <item>
242 <property name="text">
243 <string>GB-Eire</string>
244 </property>
245 </item>
246 <item>
247 <property name="text">
248 <string>GMT</string>
249 </property>
250 </item>
251 <item>
252 <property name="text">
253 <string>GMT+0</string>
254 </property>
255 </item>
256 <item>
257 <property name="text">
258 <string>GMT-0</string>
259 </property>
260 </item>
261 <item>
262 <property name="text">
263 <string>GMT0</string>
264 </property>
265 </item>
266 <item>
267 <property name="text">
268 <string>Greenwich</string>
269 </property>
270 </item>
271 <item>
272 <property name="text">
273 <string>Hongkong</string>
274 </property>
275 </item>
276 <item>
277 <property name="text">
278 <string>HST</string>
279 </property>
280 </item>
281 <item>
282 <property name="text">
283 <string>Iceland</string>
284 </property>
285 </item>
286 <item>
287 <property name="text">
288 <string>Iran</string>
289 </property>
290 </item>
291 <item>
292 <property name="text">
293 <string>Israel</string>
294 </property>
295 </item>
296 <item>
297 <property name="text">
298 <string>Jamaica</string>
299 </property>
300 </item>
301 <item>
302 <property name="text">
303 <string>Japan</string>
304 </property>
305 </item>
306 <item>
307 <property name="text">
308 <string>Kwajalein</string>
309 </property>
310 </item>
311 <item>
312 <property name="text">
313 <string>Libya</string>
314 </property>
315 </item>
316 <item>
317 <property name="text">
318 <string>MET</string>
319 </property>
320 </item>
321 <item>
322 <property name="text">
323 <string>MST</string>
324 </property>
325 </item>
326 <item>
327 <property name="text">
328 <string>MST7MDT</string>
329 </property>
330 </item>
331 <item>
332 <property name="text">
333 <string>Navajo</string>
334 </property>
335 </item>
336 <item>
337 <property name="text">
338 <string>NZ</string>
339 </property>
340 </item>
341 <item>
342 <property name="text">
343 <string>NZ-CHAT</string>
344 </property>
345 </item>
346 <item>
347 <property name="text">
348 <string>Poland</string>
349 </property>
350 </item>
351 <item>
352 <property name="text">
353 <string>Portugal</string>
354 </property>
355 </item>
356 <item>
357 <property name="text">
358 <string>PRC</string>
359 </property>
360 </item>
361 <item>
362 <property name="text">
363 <string>PST8PDT</string>
364 </property>
365 </item>
366 <item>
367 <property name="text">
368 <string>ROC</string>
369 </property>
370 </item>
371 <item>
372 <property name="text">
373 <string>ROK</string>
374 </property>
375 </item>
376 <item>
377 <property name="text">
378 <string>Singapore</string>
379 </property>
380 </item>
381 <item>
382 <property name="text">
383 <string>Turkey</string>
384 </property>
385 </item>
386 <item>
387 <property name="text">
388 <string>UCT</string>
389 </property>
390 </item>
391 <item>
392 <property name="text">
393 <string>Universal</string>
394 </property>
395 </item>
396 <item>
397 <property name="text">
398 <string>UTC</string>
399 </property>
400 </item>
401 <item>
402 <property name="text">
403 <string>W-SU</string>
404 </property>
405 </item>
406 <item>
407 <property name="text">
408 <string>WET</string>
409 </property>
410 </item>
411 <item>
412 <property name="text">
413 <string>Zulu</string>
414 </property>
415 </item>
416 </widget>
417 </item>
418 <item row="6" column="0">
178 <widget class="QCheckBox" name="rng_seed_checkbox"> 419 <widget class="QCheckBox" name="rng_seed_checkbox">
179 <property name="text"> 420 <property name="text">
180 <string>RNG Seed</string> 421 <string>RNG Seed</string>
181 </property> 422 </property>
182 </widget> 423 </widget>
183 </item> 424 </item>
184 <item row="2" column="1"> 425 <item row="3" column="1">
185 <widget class="QComboBox" name="combo_sound"> 426 <widget class="QComboBox" name="combo_sound">
186 <item> 427 <item>
187 <property name="text"> 428 <property name="text">
@@ -207,7 +448,7 @@
207 </property> 448 </property>
208 </widget> 449 </widget>
209 </item> 450 </item>
210 <item row="3" column="1"> 451 <item row="4" column="1">
211 <widget class="QPushButton" name="button_regenerate_console_id"> 452 <widget class="QPushButton" name="button_regenerate_console_id">
212 <property name="sizePolicy"> 453 <property name="sizePolicy">
213 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 454 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -223,14 +464,14 @@
223 </property> 464 </property>
224 </widget> 465 </widget>
225 </item> 466 </item>
226 <item row="4" column="0"> 467 <item row="5" column="0">
227 <widget class="QCheckBox" name="custom_rtc_checkbox"> 468 <widget class="QCheckBox" name="custom_rtc_checkbox">
228 <property name="text"> 469 <property name="text">
229 <string>Custom RTC</string> 470 <string>Custom RTC</string>
230 </property> 471 </property>
231 </widget> 472 </widget>
232 </item> 473 </item>
233 <item row="4" column="1"> 474 <item row="5" column="1">
234 <widget class="QDateTimeEdit" name="custom_rtc_edit"> 475 <widget class="QDateTimeEdit" name="custom_rtc_edit">
235 <property name="minimumDate"> 476 <property name="minimumDate">
236 <date> 477 <date>
@@ -244,7 +485,7 @@
244 </property> 485 </property>
245 </widget> 486 </widget>
246 </item> 487 </item>
247 <item row="5" column="1"> 488 <item row="6" column="1">
248 <widget class="QLineEdit" name="rng_seed_edit"> 489 <widget class="QLineEdit" name="rng_seed_edit">
249 <property name="sizePolicy"> 490 <property name="sizePolicy">
250 <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> 491 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index dccbabcbf..bfb600df0 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -488,11 +488,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
488 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 488 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
489 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); 489 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
490 490
491 connect(open_save_location, &QAction::triggered, [this, program_id]() { 491 connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
492 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); 492 emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
493 }); 493 });
494 connect(open_lfs_location, &QAction::triggered, [this, program_id]() { 494 connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() {
495 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); 495 emit OpenFolderRequested(GameListOpenTarget::ModData, path);
496 }); 496 });
497 connect(open_transferable_shader_cache, &QAction::triggered, 497 connect(open_transferable_shader_cache, &QAction::triggered,
498 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); 498 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 878d94413..a38cb2fc3 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -73,7 +73,7 @@ public:
73signals: 73signals:
74 void GameChosen(QString game_path); 74 void GameChosen(QString game_path);
75 void ShouldCancelWorker(); 75 void ShouldCancelWorker();
76 void OpenFolderRequested(u64 program_id, GameListOpenTarget target); 76 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
77 void OpenTransferableShaderCacheRequested(u64 program_id); 77 void OpenTransferableShaderCacheRequested(u64 program_id);
78 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 78 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
79 void CopyTIDRequested(u64 program_id); 79 void CopyTIDRequested(u64 program_id);
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index 2a6483370..ae842306c 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -19,6 +19,7 @@
19#include <QTime> 19#include <QTime>
20#include <QtConcurrent/QtConcurrentRun> 20#include <QtConcurrent/QtConcurrentRun>
21#include "common/logging/log.h" 21#include "common/logging/log.h"
22#include "core/frontend/framebuffer_layout.h"
22#include "core/loader/loader.h" 23#include "core/loader/loader.h"
23#include "ui_loading_screen.h" 24#include "ui_loading_screen.h"
24#include "video_core/rasterizer_interface.h" 25#include "video_core/rasterizer_interface.h"
@@ -61,7 +62,7 @@ LoadingScreen::LoadingScreen(QWidget* parent)
61 : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()), 62 : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()),
62 previous_stage(VideoCore::LoadCallbackStage::Complete) { 63 previous_stage(VideoCore::LoadCallbackStage::Complete) {
63 ui->setupUi(this); 64 ui->setupUi(this);
64 setMinimumSize(1280, 720); 65 setMinimumSize(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
65 66
66 // Create a fade out effect to hide this loading screen widget. 67 // Create a fade out effect to hide this loading screen widget.
67 // When fading opacity, it will fade to the parent widgets background color, which is why we 68 // When fading opacity, it will fade to the parent widgets background color, which is why we
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 86e8a1d49..0b291c7d0 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -724,13 +724,13 @@ void GMainWindow::InitializeHotkeys() {
724} 724}
725 725
726void GMainWindow::SetDefaultUIGeometry() { 726void GMainWindow::SetDefaultUIGeometry() {
727 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half 727 // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
728 const QRect screenRect = QApplication::desktop()->screenGeometry(this); 728 const QRect screenRect = QApplication::desktop()->screenGeometry(this);
729 729
730 const int w = screenRect.width() * 2 / 3; 730 const int w = screenRect.width() * 2 / 3;
731 const int h = screenRect.height() / 2; 731 const int h = screenRect.height() * 2 / 3;
732 const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2; 732 const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2;
733 const int y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100; 733 const int y = (screenRect.y() + screenRect.height()) / 2 - h * 53 / 100;
734 734
735 setGeometry(x, y, w, h); 735 setGeometry(x, y, w, h);
736} 736}
@@ -831,6 +831,7 @@ void GMainWindow::ConnectMenuEvents() {
831 &GMainWindow::OnDisplayTitleBars); 831 &GMainWindow::OnDisplayTitleBars);
832 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); 832 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
833 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 833 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
834 connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
834 835
835 // Fullscreen 836 // Fullscreen
836 ui.action_Fullscreen->setShortcut( 837 ui.action_Fullscreen->setShortcut(
@@ -1154,39 +1155,61 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
1154 BootGame(game_path); 1155 BootGame(game_path);
1155} 1156}
1156 1157
1157void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) { 1158void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) {
1158 std::string path; 1159 std::string path;
1159 QString open_target; 1160 QString open_target;
1161
1162 const auto v_file = Core::GetGameFileFromPath(vfs, game_path);
1163 const auto loader = Loader::GetLoader(v_file);
1164 FileSys::NACP control{};
1165 u64 program_id{};
1166
1167 loader->ReadControlData(control);
1168 loader->ReadProgramId(program_id);
1169
1170 const bool has_user_save{control.GetDefaultNormalSaveSize() > 0};
1171 const bool has_device_save{control.GetDeviceSaveDataSize() > 0};
1172
1173 ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
1174
1160 switch (target) { 1175 switch (target) {
1161 case GameListOpenTarget::SaveData: { 1176 case GameListOpenTarget::SaveData: {
1162 open_target = tr("Save Data"); 1177 open_target = tr("Save Data");
1163 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 1178 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
1164 ASSERT(program_id != 0); 1179 ASSERT(program_id != 0);
1165 1180
1166 const auto select_profile = [this] { 1181 if (has_user_save) {
1167 QtProfileSelectionDialog dialog(this); 1182 // User save data
1168 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 1183 const auto select_profile = [this] {
1169 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 1184 QtProfileSelectionDialog dialog(this);
1170 dialog.setWindowModality(Qt::WindowModal); 1185 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
1186 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
1187 dialog.setWindowModality(Qt::WindowModal);
1171 1188
1172 if (dialog.exec() == QDialog::Rejected) { 1189 if (dialog.exec() == QDialog::Rejected) {
1173 return -1; 1190 return -1;
1174 } 1191 }
1175 1192
1176 return dialog.GetIndex(); 1193 return dialog.GetIndex();
1177 }; 1194 };
1178 1195
1179 const auto index = select_profile(); 1196 const auto index = select_profile();
1180 if (index == -1) { 1197 if (index == -1) {
1181 return; 1198 return;
1182 } 1199 }
1183 1200
1184 Service::Account::ProfileManager manager; 1201 Service::Account::ProfileManager manager;
1185 const auto user_id = manager.GetUser(static_cast<std::size_t>(index)); 1202 const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
1186 ASSERT(user_id); 1203 ASSERT(user_id);
1187 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, 1204 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
1188 FileSys::SaveDataType::SaveData, 1205 FileSys::SaveDataSpaceId::NandUser,
1189 program_id, user_id->uuid, 0); 1206 FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0);
1207 } else {
1208 // Device save data
1209 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
1210 FileSys::SaveDataSpaceId::NandUser,
1211 FileSys::SaveDataType::SaveData, program_id, {}, 0);
1212 }
1190 1213
1191 if (!FileUtil::Exists(path)) { 1214 if (!FileUtil::Exists(path)) {
1192 FileUtil::CreateFullPath(path); 1215 FileUtil::CreateFullPath(path);
@@ -1829,6 +1852,20 @@ void GMainWindow::ToggleWindowMode() {
1829 } 1852 }
1830} 1853}
1831 1854
1855void GMainWindow::ResetWindowSize() {
1856 const auto aspect_ratio = Layout::EmulationAspectRatio(
1857 static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio),
1858 static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width);
1859 if (!ui.action_Single_Window_Mode->isChecked()) {
1860 render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio,
1861 Layout::ScreenUndocked::Height);
1862 } else {
1863 resize(Layout::ScreenUndocked::Height / aspect_ratio,
1864 Layout::ScreenUndocked::Height + menuBar()->height() +
1865 (ui.action_Show_Status_Bar->isChecked() ? statusBar()->height() : 0));
1866 }
1867}
1868
1832void GMainWindow::OnConfigure() { 1869void GMainWindow::OnConfigure() {
1833 const auto old_theme = UISettings::values.theme; 1870 const auto old_theme = UISettings::values.theme;
1834 const bool old_discord_presence = UISettings::values.enable_discord_presence; 1871 const bool old_discord_presence = UISettings::values.enable_discord_presence;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 60b17c54a..4f4c8ddbe 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -183,7 +183,7 @@ private slots:
183 void OnMenuReportCompatibility(); 183 void OnMenuReportCompatibility();
184 /// Called whenever a user selects a game in the game list widget. 184 /// Called whenever a user selects a game in the game list widget.
185 void OnGameListLoadFile(QString game_path); 185 void OnGameListLoadFile(QString game_path);
186 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); 186 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
187 void OnTransferableShaderCacheOpenFile(u64 program_id); 187 void OnTransferableShaderCacheOpenFile(u64 program_id);
188 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 188 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
189 void OnGameListCopyTID(u64 program_id); 189 void OnGameListCopyTID(u64 program_id);
@@ -208,6 +208,7 @@ private slots:
208 void ShowFullscreen(); 208 void ShowFullscreen();
209 void HideFullscreen(); 209 void HideFullscreen();
210 void ToggleWindowMode(); 210 void ToggleWindowMode();
211 void ResetWindowSize();
211 void OnCaptureScreenshot(); 212 void OnCaptureScreenshot();
212 void OnCoreError(Core::System::ResultStatus, std::string); 213 void OnCoreError(Core::System::ResultStatus, std::string);
213 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 214 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index ae414241e..97c90f50b 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>1081</width> 9 <width>1280</width>
10 <height>730</height> 10 <height>720</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -44,7 +44,7 @@
44 <rect> 44 <rect>
45 <x>0</x> 45 <x>0</x>
46 <y>0</y> 46 <y>0</y>
47 <width>1081</width> 47 <width>1280</width>
48 <height>21</height> 48 <height>21</height>
49 </rect> 49 </rect>
50 </property> 50 </property>
@@ -96,6 +96,7 @@
96 <addaction name="action_Display_Dock_Widget_Headers"/> 96 <addaction name="action_Display_Dock_Widget_Headers"/>
97 <addaction name="action_Show_Filter_Bar"/> 97 <addaction name="action_Show_Filter_Bar"/>
98 <addaction name="action_Show_Status_Bar"/> 98 <addaction name="action_Show_Status_Bar"/>
99 <addaction name="action_Reset_Window_Size"/>
99 <addaction name="separator"/> 100 <addaction name="separator"/>
100 <addaction name="menu_View_Debugging"/> 101 <addaction name="menu_View_Debugging"/>
101 </widget> 102 </widget>
@@ -215,6 +216,11 @@
215 <string>Show Status Bar</string> 216 <string>Show Status Bar</string>
216 </property> 217 </property>
217 </action> 218 </action>
219 <action name="action_Reset_Window_Size">
220 <property name="text">
221 <string>Reset Window Size</string>
222 </property>
223 </action>
218 <action name="action_Fullscreen"> 224 <action name="action_Fullscreen">
219 <property name="checkable"> 225 <property name="checkable">
220 <bool>true</bool> 226 <bool>true</bool>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 8476a5a16..2348e6e0d 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -367,6 +367,9 @@ void Config::ReadValues() {
367 Settings::values.custom_rtc = std::nullopt; 367 Settings::values.custom_rtc = std::nullopt;
368 } 368 }
369 369
370 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
371 Settings::values.time_zone_index = sdl2_config->GetInteger("System", "time_zone_index", 0);
372
370 // Core 373 // Core
371 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 374 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
372 375
@@ -409,8 +412,6 @@ void Config::ReadValues() {
409 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); 412 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
410 Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); 413 Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1));
411 414
412 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
413
414 // Miscellaneous 415 // Miscellaneous
415 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 416 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
416 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 417 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 60b1a62fa..ae94b51c4 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -262,6 +262,10 @@ language_index =
262# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan 262# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
263region_value = 263region_value =
264 264
265# The system time zone that yuzu will use during emulation
266# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
267time_zone_index =
268
265[Miscellaneous] 269[Miscellaneous]
266# A filter which removes logs below a certain logging level. 270# A filter which removes logs below a certain logging level.
267# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical 271# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical