summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/logging/backend.cpp11
-rw-r--r--src/common/logging/backend.h14
-rw-r--r--src/common/telemetry.h4
-rw-r--r--src/core/hle/service/acc/profile_manager.h3
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp3
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h4
-rw-r--r--src/core/telemetry_session.cpp9
-rw-r--r--src/core/telemetry_session.h6
-rw-r--r--src/video_core/engines/maxwell_3d.cpp26
-rw-r--r--src/video_core/engines/maxwell_3d.h25
-rw-r--r--src/video_core/engines/shader_bytecode.h7
-rw-r--r--src/video_core/macro_interpreter.cpp19
-rw-r--r--src/video_core/macro_interpreter.h12
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp38
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp126
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp246
-rw-r--r--src/video_core/renderer_opengl/gl_state.h13
-rw-r--r--src/video_core/surface.cpp26
-rw-r--r--src/video_core/surface.h107
-rw-r--r--src/video_core/textures/astc.cpp32
-rw-r--r--src/video_core/textures/astc.h2
-rw-r--r--src/video_core/textures/decoders.cpp12
-rw-r--r--src/video_core/textures/decoders.h4
-rw-r--r--src/web_service/telemetry_json.cpp21
-rw-r--r--src/web_service/telemetry_json.h1
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/compatdb.cpp27
-rw-r--r--src/yuzu/compatdb.h4
-rw-r--r--src/yuzu/configuration/configure_system.cpp43
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
-rw-r--r--src/yuzu/main.cpp11
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp59
-rw-r--r--src/yuzu/util/limitable_input_dialog.h31
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
40 files changed, 726 insertions, 248 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 6d5218465..5753b871a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -12,7 +12,8 @@
12#include <thread> 12#include <thread>
13#include <vector> 13#include <vector>
14#ifdef _WIN32 14#ifdef _WIN32
15#include <share.h> // For _SH_DENYWR 15#include <share.h> // For _SH_DENYWR
16#include <windows.h> // For OutputDebugStringA
16#else 17#else
17#define _SH_DENYWR 0 18#define _SH_DENYWR 0
18#endif 19#endif
@@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
139 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 140 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
140 return; 141 return;
141 } 142 }
142 bytes_written += file.WriteString(FormatLogMessage(entry) + '\n'); 143 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
143 if (entry.log_level >= Level::Error) { 144 if (entry.log_level >= Level::Error) {
144 file.Flush(); 145 file.Flush();
145 } 146 }
146} 147}
147 148
149void DebuggerBackend::Write(const Entry& entry) {
150#ifdef _WIN32
151 ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
152#endif
153}
154
148/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. 155/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
149#define ALL_LOG_CLASSES() \ 156#define ALL_LOG_CLASSES() \
150 CLS(Log) \ 157 CLS(Log) \
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 11edbf1b6..91bb0c309 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -103,6 +103,20 @@ private:
103 std::size_t bytes_written; 103 std::size_t bytes_written;
104}; 104};
105 105
106/**
107 * Backend that writes to Visual Studio's output window
108 */
109class DebuggerBackend : public Backend {
110public:
111 static const char* Name() {
112 return "debugger";
113 }
114 const char* GetName() const override {
115 return Name();
116 }
117 void Write(const Entry& entry) override;
118};
119
106void AddBackend(std::unique_ptr<Backend> backend); 120void AddBackend(std::unique_ptr<Backend> backend);
107 121
108void RemoveBackend(std::string_view backend_name); 122void RemoveBackend(std::string_view backend_name);
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 8d6ab986b..854a73fae 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
153 153
154 /// Completion method, called once all fields have been visited 154 /// Completion method, called once all fields have been visited
155 virtual void Complete() = 0; 155 virtual void Complete() = 0;
156 virtual bool SubmitTestcase() = 0;
156}; 157};
157 158
158/** 159/**
@@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
178 void Visit(const Field<std::chrono::microseconds>& /*field*/) override {} 179 void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
179 180
180 void Complete() override {} 181 void Complete() override {}
182 bool SubmitTestcase() override {
183 return false;
184 }
181}; 185};
182 186
183/// Appends build-specific information to the given FieldCollection, 187/// Appends build-specific information to the given FieldCollection,
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 1cd2e51b2..747c46c20 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -57,7 +57,8 @@ struct UUID {
57}; 57};
58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
59 59
60using ProfileUsername = std::array<u8, 0x20>; 60constexpr std::size_t profile_username_size = 32;
61using ProfileUsername = std::array<u8, profile_username_size>;
61using ProfileData = std::array<u8, MAX_DATA>; 62using ProfileData = std::array<u8, MAX_DATA>;
62using UserIDArray = std::array<UUID, MAX_USERS>; 63using UserIDArray = std::array<UUID, MAX_USERS>;
63 64
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 7168c6a10..783c39503 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
161 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 161 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
162 162
163 std::size_t worker_sz = WorkerBufferSize(channel_count); 163 std::size_t worker_sz = WorkerBufferSize(channel_count);
164 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); 164 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
165 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ 165 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
166 static_cast<OpusDecoder*>(operator new(worker_sz))}; 166 static_cast<OpusDecoder*>(operator new(worker_sz))};
167 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 167 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 4b4d1324f..1ef789bd0 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -427,6 +427,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
427} 427}
428 428
429Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { 429Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
430 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
431 // be signalled at least once, and signaled after a new controller is connected?
432 styleset_changed_event->Signal();
430 return styleset_changed_event; 433 return styleset_changed_event;
431} 434}
432 435
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a9aa9ec78..a45fd4954 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -96,6 +96,8 @@ public:
96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
97 97
98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
99
100 ReloadInputDevices();
99 } 101 }
100 102
101 void ActivateController(HidController controller) { 103 void ActivateController(HidController controller) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 2fe81a560..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -58,9 +58,9 @@ public:
58 /// Rotate source image 90 degrees clockwise 58 /// Rotate source image 90 degrees clockwise
59 Rotate90 = 0x04, 59 Rotate90 = 0x04,
60 /// Rotate source image 180 degrees 60 /// Rotate source image 180 degrees
61 Roate180 = 0x03, 61 Rotate180 = 0x03,
62 /// Rotate source image 270 degrees clockwise 62 /// Rotate source image 270 degrees clockwise
63 Roate270 = 0x07, 63 Rotate270 = 0x07,
64 }; 64 };
65 65
66 struct Buffer { 66 struct Buffer {
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 0de13edd3..a3b08c740 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -184,4 +184,13 @@ TelemetrySession::~TelemetrySession() {
184 backend = nullptr; 184 backend = nullptr;
185} 185}
186 186
187bool TelemetrySession::SubmitTestcase() {
188#ifdef ENABLE_WEB_SERVICE
189 field_collection.Accept(*backend);
190 return backend->SubmitTestcase();
191#else
192 return false;
193#endif
194}
195
187} // namespace Core 196} // namespace Core
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 2a4845797..023612b79 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -31,6 +31,12 @@ public:
31 field_collection.AddField(type, name, std::move(value)); 31 field_collection.AddField(type, name, std::move(value));
32 } 32 }
33 33
34 /**
35 * Submits a Testcase.
36 * @returns A bool indicating whether the submission succeeded
37 */
38 bool SubmitTestcase();
39
34private: 40private:
35 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 41 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
36 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields 42 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 7357d20d1..d79c50919 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -43,15 +43,17 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
43 // Reset the current macro. 43 // Reset the current macro.
44 executing_macro = 0; 44 executing_macro = 0;
45 45
46 // The requested macro must have been uploaded already. 46 // Lookup the macro offset
47 auto macro_code = uploaded_macros.find(method); 47 const u32 entry{(method - MacroRegistersStart) >> 1};
48 if (macro_code == uploaded_macros.end()) { 48 const auto& search{macro_offsets.find(entry)};
49 LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); 49 if (search == macro_offsets.end()) {
50 LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
51 UNREACHABLE();
50 return; 52 return;
51 } 53 }
52 54
53 // Execute the current macro. 55 // Execute the current macro.
54 macro_interpreter.Execute(macro_code->second, std::move(parameters)); 56 macro_interpreter.Execute(search->second, std::move(parameters));
55} 57}
56 58
57void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { 59void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@@ -97,6 +99,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
97 ProcessMacroUpload(value); 99 ProcessMacroUpload(value);
98 break; 100 break;
99 } 101 }
102 case MAXWELL3D_REG_INDEX(macros.bind): {
103 ProcessMacroBind(value);
104 break;
105 }
100 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): 106 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
101 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): 107 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
102 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): 108 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
@@ -158,9 +164,13 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
158} 164}
159 165
160void Maxwell3D::ProcessMacroUpload(u32 data) { 166void Maxwell3D::ProcessMacroUpload(u32 data) {
161 // Store the uploaded macro code to interpret them when they're called. 167 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
162 auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart]; 168 "upload_address exceeded macro_memory size!");
163 macro.push_back(data); 169 macro_memory[regs.macros.upload_address++] = data;
170}
171
172void Maxwell3D::ProcessMacroBind(u32 data) {
173 macro_offsets[regs.macros.entry] = data;
164} 174}
165 175
166void Maxwell3D::ProcessQueryGet() { 176void Maxwell3D::ProcessQueryGet() {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 443affc36..50873813e 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -475,12 +475,13 @@ public:
475 INSERT_PADDING_WORDS(0x45); 475 INSERT_PADDING_WORDS(0x45);
476 476
477 struct { 477 struct {
478 INSERT_PADDING_WORDS(1); 478 u32 upload_address;
479 u32 data; 479 u32 data;
480 u32 entry; 480 u32 entry;
481 u32 bind;
481 } macros; 482 } macros;
482 483
483 INSERT_PADDING_WORDS(0x189); 484 INSERT_PADDING_WORDS(0x188);
484 485
485 u32 tfb_enabled; 486 u32 tfb_enabled;
486 487
@@ -994,12 +995,25 @@ public:
994 /// Returns the texture information for a specific texture in a specific shader stage. 995 /// Returns the texture information for a specific texture in a specific shader stage.
995 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; 996 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
996 997
998 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
999 /// we've seen used.
1000 using MacroMemory = std::array<u32, 0x40000>;
1001
1002 /// Gets a reference to macro memory.
1003 const MacroMemory& GetMacroMemory() const {
1004 return macro_memory;
1005 }
1006
997private: 1007private:
998 void InitializeRegisterDefaults(); 1008 void InitializeRegisterDefaults();
999 1009
1000 VideoCore::RasterizerInterface& rasterizer; 1010 VideoCore::RasterizerInterface& rasterizer;
1001 1011
1002 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 1012 /// Start offsets of each macro in macro_memory
1013 std::unordered_map<u32, u32> macro_offsets;
1014
1015 /// Memory for macro code
1016 MacroMemory macro_memory;
1003 1017
1004 /// Macro method that is currently being executed / being fed parameters. 1018 /// Macro method that is currently being executed / being fed parameters.
1005 u32 executing_macro = 0; 1019 u32 executing_macro = 0;
@@ -1022,9 +1036,12 @@ private:
1022 */ 1036 */
1023 void CallMacroMethod(u32 method, std::vector<u32> parameters); 1037 void CallMacroMethod(u32 method, std::vector<u32> parameters);
1024 1038
1025 /// Handles writes to the macro uploading registers. 1039 /// Handles writes to the macro uploading register.
1026 void ProcessMacroUpload(u32 data); 1040 void ProcessMacroUpload(u32 data);
1027 1041
1042 /// Handles writes to the macro bind register.
1043 void ProcessMacroBind(u32 data);
1044
1028 /// Handles a write to the CLEAR_BUFFERS register. 1045 /// Handles a write to the CLEAR_BUFFERS register.
1029 void ProcessClearBuffers(); 1046 void ProcessClearBuffers();
1030 1047
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b84da512f..83a6fd875 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -578,6 +578,10 @@ union Instruction {
578 } fmul32; 578 } fmul32;
579 579
580 union { 580 union {
581 BitField<52, 1, u64> generates_cc;
582 } op_32;
583
584 union {
581 BitField<48, 1, u64> is_signed; 585 BitField<48, 1, u64> is_signed;
582 } shift; 586 } shift;
583 587
@@ -1231,6 +1235,7 @@ union Instruction {
1231 BitField<60, 1, u64> is_b_gpr; 1235 BitField<60, 1, u64> is_b_gpr;
1232 BitField<59, 1, u64> is_c_gpr; 1236 BitField<59, 1, u64> is_c_gpr;
1233 BitField<20, 24, s64> smem_imm; 1237 BitField<20, 24, s64> smem_imm;
1238 BitField<0, 5, ControlCode> flow_control_code;
1234 1239
1235 Attribute attribute; 1240 Attribute attribute;
1236 Sampler sampler; 1241 Sampler sampler;
@@ -1658,4 +1663,4 @@ private:
1658 } 1663 }
1659}; 1664};
1660 1665
1661} // namespace Tegra::Shader \ No newline at end of file 1666} // namespace Tegra::Shader
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index f6af132fb..335a8d407 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -11,7 +11,7 @@ namespace Tegra {
11 11
12MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} 12MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
13 13
14void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) { 14void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
15 Reset(); 15 Reset();
16 registers[1] = parameters[0]; 16 registers[1] = parameters[0];
17 this->parameters = std::move(parameters); 17 this->parameters = std::move(parameters);
@@ -19,7 +19,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
19 // Execute the code until we hit an exit condition. 19 // Execute the code until we hit an exit condition.
20 bool keep_executing = true; 20 bool keep_executing = true;
21 while (keep_executing) { 21 while (keep_executing) {
22 keep_executing = Step(code, false); 22 keep_executing = Step(offset, false);
23 } 23 }
24 24
25 // Assert the the macro used all the input parameters 25 // Assert the the macro used all the input parameters
@@ -37,10 +37,10 @@ void MacroInterpreter::Reset() {
37 next_parameter_index = 1; 37 next_parameter_index = 1;
38} 38}
39 39
40bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) { 40bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
41 u32 base_address = pc; 41 u32 base_address = pc;
42 42
43 Opcode opcode = GetOpcode(code); 43 Opcode opcode = GetOpcode(offset);
44 pc += 4; 44 pc += 4;
45 45
46 // Update the program counter if we were delayed 46 // Update the program counter if we were delayed
@@ -108,7 +108,7 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
108 108
109 delayed_pc = base_address + opcode.GetBranchTarget(); 109 delayed_pc = base_address + opcode.GetBranchTarget();
110 // Execute one more instruction due to the delay slot. 110 // Execute one more instruction due to the delay slot.
111 return Step(code, true); 111 return Step(offset, true);
112 } 112 }
113 break; 113 break;
114 } 114 }
@@ -121,17 +121,18 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
121 // Exit has a delay slot, execute the next instruction 121 // Exit has a delay slot, execute the next instruction
122 // Note: Executing an exit during a branch delay slot will cause the instruction at the 122 // Note: Executing an exit during a branch delay slot will cause the instruction at the
123 // branch target to be executed before exiting. 123 // branch target to be executed before exiting.
124 Step(code, true); 124 Step(offset, true);
125 return false; 125 return false;
126 } 126 }
127 127
128 return true; 128 return true;
129} 129}
130 130
131MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const { 131MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
132 const auto& macro_memory{maxwell3d.GetMacroMemory()};
132 ASSERT((pc % sizeof(u32)) == 0); 133 ASSERT((pc % sizeof(u32)) == 0);
133 ASSERT(pc < code.size() * sizeof(u32)); 134 ASSERT((pc + offset) < macro_memory.size() * sizeof(u32));
134 return {code[pc / sizeof(u32)]}; 135 return {macro_memory[offset + pc / sizeof(u32)]};
135} 136}
136 137
137u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { 138u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index 773684bde..62d1ce289 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -22,10 +22,10 @@ public:
22 22
23 /** 23 /**
24 * Executes the macro code with the specified input parameters. 24 * Executes the macro code with the specified input parameters.
25 * @param code The macro byte code to execute 25 * @param offset Offset to start execution at.
26 * @param parameters The parameters of the macro 26 * @param parameters The parameters of the macro.
27 */ 27 */
28 void Execute(const std::vector<u32>& code, std::vector<u32> parameters); 28 void Execute(u32 offset, std::vector<u32> parameters);
29 29
30private: 30private:
31 enum class Operation : u32 { 31 enum class Operation : u32 {
@@ -110,11 +110,11 @@ private:
110 /** 110 /**
111 * Executes a single macro instruction located at the current program counter. Returns whether 111 * Executes a single macro instruction located at the current program counter. Returns whether
112 * the interpreter should keep running. 112 * the interpreter should keep running.
113 * @param code The macro code to execute. 113 * @param offset Offset to start execution at.
114 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a 114 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a
115 * previous instruction. 115 * previous instruction.
116 */ 116 */
117 bool Step(const std::vector<u32>& code, bool is_delay_slot); 117 bool Step(u32 offset, bool is_delay_slot);
118 118
119 /// Calculates the result of an ALU operation. src_a OP src_b; 119 /// Calculates the result of an ALU operation. src_a OP src_b;
120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; 120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
@@ -127,7 +127,7 @@ private:
127 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; 127 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
128 128
129 /// Reads an opcode at the current program counter location. 129 /// Reads an opcode at the current program counter location.
130 Opcode GetOpcode(const std::vector<u32>& code) const; 130 Opcode GetOpcode(u32 offset) const;
131 131
132 /// Returns the specified register's value. Register 0 is hardcoded to always return 0. 132 /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
133 u32 GetRegister(u32 register_id) const; 133 u32 GetRegister(u32 register_id) const;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 75e31c6de..a0527fe57 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -104,7 +104,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
104 } 104 }
105 105
106 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); 106 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
107 107 OpenGLState::ApplyDefaultState();
108 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 108 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
109 state.clip_distance[0] = true; 109 state.clip_distance[0] = true;
110 110
@@ -115,8 +115,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
115 state.draw.shader_program = 0; 115 state.draw.shader_program = 0;
116 state.Apply(); 116 state.Apply();
117 117
118 glEnable(GL_BLEND);
119
120 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); 118 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
121 119
122 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); 120 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!");
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 752c4ee84..dcbf009c0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -58,16 +58,14 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
58 58
59std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only, 59std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
60 bool uncompressed) const { 60 bool uncompressed) const {
61 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 61 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
62 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
62 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; 63 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
63 u32 m_depth = (layer_only ? 1U : depth); 64 u32 m_depth = (layer_only ? 1U : depth);
64 u32 m_width = MipWidth(mip_level); 65 u32 m_width = MipWidth(mip_level);
65 u32 m_height = MipHeight(mip_level); 66 u32 m_height = MipHeight(mip_level);
66 m_width = uncompressed ? m_width 67 m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
67 : std::max(1U, (m_width + compression_factor - 1) / compression_factor); 68 m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
68 m_height = uncompressed
69 ? m_height
70 : std::max(1U, (m_height + compression_factor - 1) / compression_factor);
71 m_depth = std::max(1U, m_depth >> mip_level); 69 m_depth = std::max(1U, m_depth >> mip_level);
72 u32 m_block_height = MipBlockHeight(mip_level); 70 u32 m_block_height = MipBlockHeight(mip_level);
73 u32 m_block_depth = MipBlockDepth(mip_level); 71 u32 m_block_depth = MipBlockDepth(mip_level);
@@ -312,6 +310,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
312 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB 310 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
313 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB 311 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB 312 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
313 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
315 315
316 // Depth formats 316 // Depth formats
317 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 317 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -373,15 +373,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
373 373
374 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 374 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
375 // pixel values. 375 // pixel values.
376 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 376 const u32 tile_size_x{GetDefaultBlockWidth(format)};
377 const u32 tile_size_y{GetDefaultBlockHeight(format)};
377 378
378 if (morton_to_gl) { 379 if (morton_to_gl) {
379 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 380 const std::vector<u8> data =
380 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 381 Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
382 stride, height, depth, block_height, block_depth);
381 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 383 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
382 memcpy(gl_buffer, data.data(), size_to_copy); 384 memcpy(gl_buffer, data.data(), size_to_copy);
383 } else { 385 } else {
384 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth, 386 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
387 (height + tile_size_y - 1) / tile_size_y, depth,
385 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), 388 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
386 gl_buffer, false, block_height, block_depth); 389 gl_buffer, false, block_height, block_depth);
387 } 390 }
@@ -449,6 +452,8 @@ static constexpr GLConversionArray morton_to_gl_fns = {
449 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, 452 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
450 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, 453 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
451 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 454 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
455 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
456 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
452 MortonCopy<true, PixelFormat::Z32F>, 457 MortonCopy<true, PixelFormat::Z32F>,
453 MortonCopy<true, PixelFormat::Z16>, 458 MortonCopy<true, PixelFormat::Z16>,
454 MortonCopy<true, PixelFormat::Z24S8>, 459 MortonCopy<true, PixelFormat::Z24S8>,
@@ -517,6 +522,8 @@ static constexpr GLConversionArray gl_to_morton_fns = {
517 nullptr, 522 nullptr,
518 nullptr, 523 nullptr,
519 nullptr, 524 nullptr,
525 nullptr,
526 nullptr,
520 MortonCopy<false, PixelFormat::Z32F>, 527 MortonCopy<false, PixelFormat::Z32F>,
521 MortonCopy<false, PixelFormat::Z16>, 528 MortonCopy<false, PixelFormat::Z16>,
522 MortonCopy<false, PixelFormat::Z24S8>, 529 MortonCopy<false, PixelFormat::Z24S8>,
@@ -908,21 +915,24 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
908 * typical desktop GPUs. 915 * typical desktop GPUs.
909 */ 916 */
910static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 917static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
911 u32 width, u32 height) { 918 u32 width, u32 height, u32 depth) {
912 switch (pixel_format) { 919 switch (pixel_format) {
913 case PixelFormat::ASTC_2D_4X4: 920 case PixelFormat::ASTC_2D_4X4:
914 case PixelFormat::ASTC_2D_8X8: 921 case PixelFormat::ASTC_2D_8X8:
915 case PixelFormat::ASTC_2D_8X5: 922 case PixelFormat::ASTC_2D_8X5:
916 case PixelFormat::ASTC_2D_5X4: 923 case PixelFormat::ASTC_2D_5X4:
924 case PixelFormat::ASTC_2D_5X5:
917 case PixelFormat::ASTC_2D_4X4_SRGB: 925 case PixelFormat::ASTC_2D_4X4_SRGB:
918 case PixelFormat::ASTC_2D_8X8_SRGB: 926 case PixelFormat::ASTC_2D_8X8_SRGB:
919 case PixelFormat::ASTC_2D_8X5_SRGB: 927 case PixelFormat::ASTC_2D_8X5_SRGB:
920 case PixelFormat::ASTC_2D_5X4_SRGB: { 928 case PixelFormat::ASTC_2D_5X4_SRGB:
929 case PixelFormat::ASTC_2D_5X5_SRGB: {
921 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 930 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
922 u32 block_width{}; 931 u32 block_width{};
923 u32 block_height{}; 932 u32 block_height{};
924 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); 933 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
925 data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height); 934 data =
935 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
926 break; 936 break;
927 } 937 }
928 case PixelFormat::S8Z24: 938 case PixelFormat::S8Z24:
@@ -982,7 +992,7 @@ void CachedSurface::LoadGLBuffer() {
982 } 992 }
983 for (u32 i = 0; i < params.max_mip_level; i++) 993 for (u32 i = 0; i < params.max_mip_level; i++)
984 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i), 994 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
985 params.MipHeight(i)); 995 params.MipHeight(i), params.MipDepth(i));
986} 996}
987 997
988MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 998MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 5a5f2cec0..c0b6bc4e6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -141,7 +141,7 @@ struct SurfaceParams {
141 } 141 }
142 142
143 u32 MipDepth(u32 mip_level) const { 143 u32 MipDepth(u32 mip_level) const {
144 return std::max(1U, depth >> mip_level); 144 return is_layered ? depth : std::max(1U, depth >> mip_level);
145 } 145 }
146 146
147 // Auto block resizing algorithm from: 147 // Auto block resizing algorithm from:
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index d1f6ffe40..09b003c59 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -373,6 +373,7 @@ public:
373 if (sets_cc) { 373 if (sets_cc) {
374 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; 374 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
375 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); 375 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
376 LOG_WARNING(HW_GPU, "Control Codes Imcomplete.");
376 } 377 }
377 } 378 }
378 379
@@ -1525,6 +1526,10 @@ private:
1525 1526
1526 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1527 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1527 instr.alu.saturate_d, 0, true); 1528 instr.alu.saturate_d, 0, true);
1529 if (instr.generates_cc) {
1530 LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code");
1531 UNREACHABLE();
1532 }
1528 break; 1533 break;
1529 } 1534 }
1530 case OpCode::Id::FADD_C: 1535 case OpCode::Id::FADD_C:
@@ -1535,6 +1540,10 @@ private:
1535 1540
1536 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 1541 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
1537 instr.alu.saturate_d, 0, true); 1542 instr.alu.saturate_d, 0, true);
1543 if (instr.generates_cc) {
1544 LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code");
1545 UNREACHABLE();
1546 }
1538 break; 1547 break;
1539 } 1548 }
1540 case OpCode::Id::MUFU: { 1549 case OpCode::Id::MUFU: {
@@ -1588,6 +1597,10 @@ private:
1588 '(' + condition + ") ? min(" + parameters + ") : max(" + 1597 '(' + condition + ") ? min(" + parameters + ") : max(" +
1589 parameters + ')', 1598 parameters + ')',
1590 1, 1, false, 0, true); 1599 1, 1, false, 0, true);
1600 if (instr.generates_cc) {
1601 LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code");
1602 UNREACHABLE();
1603 }
1591 break; 1604 break;
1592 } 1605 }
1593 case OpCode::Id::RRO_C: 1606 case OpCode::Id::RRO_C:
@@ -1618,6 +1631,10 @@ private:
1618 regs.GetRegisterAsFloat(instr.gpr8) + " * " + 1631 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1619 GetImmediate32(instr), 1632 GetImmediate32(instr),
1620 1, 1, instr.fmul32.saturate, 0, true); 1633 1, 1, instr.fmul32.saturate, 0, true);
1634 if (instr.op_32.generates_cc) {
1635 LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code");
1636 UNREACHABLE();
1637 }
1621 break; 1638 break;
1622 } 1639 }
1623 case OpCode::Id::FADD32I: { 1640 case OpCode::Id::FADD32I: {
@@ -1641,6 +1658,10 @@ private:
1641 } 1658 }
1642 1659
1643 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); 1660 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
1661 if (instr.op_32.generates_cc) {
1662 LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code");
1663 UNREACHABLE();
1664 }
1644 break; 1665 break;
1645 } 1666 }
1646 } 1667 }
@@ -1661,6 +1682,10 @@ private:
1661 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; 1682 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
1662 1683
1663 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); 1684 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
1685 if (instr.generates_cc) {
1686 LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code");
1687 UNREACHABLE();
1688 }
1664 break; 1689 break;
1665 } 1690 }
1666 default: { 1691 default: {
@@ -1698,12 +1723,20 @@ private:
1698 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift 1723 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
1699 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1724 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1700 1, 1); 1725 1, 1);
1726 if (instr.generates_cc) {
1727 LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
1728 UNREACHABLE();
1729 }
1701 break; 1730 break;
1702 } 1731 }
1703 case OpCode::Id::SHL_C: 1732 case OpCode::Id::SHL_C:
1704 case OpCode::Id::SHL_R: 1733 case OpCode::Id::SHL_R:
1705 case OpCode::Id::SHL_IMM: 1734 case OpCode::Id::SHL_IMM:
1706 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1735 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
1736 if (instr.generates_cc) {
1737 LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code");
1738 UNREACHABLE();
1739 }
1707 break; 1740 break;
1708 default: { 1741 default: {
1709 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); 1742 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName());
@@ -1723,6 +1756,10 @@ private:
1723 1756
1724 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1757 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1725 instr.iadd32i.saturate != 0); 1758 instr.iadd32i.saturate != 0);
1759 if (instr.op_32.generates_cc) {
1760 LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code");
1761 UNREACHABLE();
1762 }
1726 break; 1763 break;
1727 case OpCode::Id::LOP32I: { 1764 case OpCode::Id::LOP32I: {
1728 if (instr.alu.lop32i.invert_a) 1765 if (instr.alu.lop32i.invert_a)
@@ -1734,6 +1771,10 @@ private:
1734 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, 1771 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1735 Tegra::Shader::PredicateResultMode::None, 1772 Tegra::Shader::PredicateResultMode::None,
1736 Tegra::Shader::Pred::UnusedIndex); 1773 Tegra::Shader::Pred::UnusedIndex);
1774 if (instr.op_32.generates_cc) {
1775 LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code");
1776 UNREACHABLE();
1777 }
1737 break; 1778 break;
1738 } 1779 }
1739 default: { 1780 default: {
@@ -1770,6 +1811,10 @@ private:
1770 1811
1771 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1812 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1772 instr.alu.saturate_d); 1813 instr.alu.saturate_d);
1814 if (instr.generates_cc) {
1815 LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code");
1816 UNREACHABLE();
1817 }
1773 break; 1818 break;
1774 } 1819 }
1775 case OpCode::Id::IADD3_C: 1820 case OpCode::Id::IADD3_C:
@@ -1831,6 +1876,11 @@ private:
1831 } 1876 }
1832 1877
1833 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); 1878 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
1879
1880 if (instr.generates_cc) {
1881 LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code");
1882 UNREACHABLE();
1883 }
1834 break; 1884 break;
1835 } 1885 }
1836 case OpCode::Id::ISCADD_C: 1886 case OpCode::Id::ISCADD_C:
@@ -1846,6 +1896,10 @@ private:
1846 1896
1847 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1897 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1848 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1898 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
1899 if (instr.generates_cc) {
1900 LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code");
1901 UNREACHABLE();
1902 }
1849 break; 1903 break;
1850 } 1904 }
1851 case OpCode::Id::POPC_C: 1905 case OpCode::Id::POPC_C:
@@ -1877,6 +1931,10 @@ private:
1877 1931
1878 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, 1932 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1879 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); 1933 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
1934 if (instr.generates_cc) {
1935 LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code");
1936 UNREACHABLE();
1937 }
1880 break; 1938 break;
1881 } 1939 }
1882 case OpCode::Id::LOP3_C: 1940 case OpCode::Id::LOP3_C:
@@ -1892,6 +1950,10 @@ private:
1892 } 1950 }
1893 1951
1894 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); 1952 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
1953 if (instr.generates_cc) {
1954 LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code");
1955 UNREACHABLE();
1956 }
1895 break; 1957 break;
1896 } 1958 }
1897 case OpCode::Id::IMNMX_C: 1959 case OpCode::Id::IMNMX_C:
@@ -1906,6 +1968,10 @@ private:
1906 '(' + condition + ") ? min(" + parameters + ") : max(" + 1968 '(' + condition + ") ? min(" + parameters + ") : max(" +
1907 parameters + ')', 1969 parameters + ')',
1908 1, 1); 1970 1, 1);
1971 if (instr.generates_cc) {
1972 LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
1973 UNREACHABLE();
1974 }
1909 break; 1975 break;
1910 } 1976 }
1911 case OpCode::Id::LEA_R2: 1977 case OpCode::Id::LEA_R2:
@@ -2107,6 +2173,10 @@ private:
2107 2173
2108 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', 2174 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
2109 1, 1, instr.alu.saturate_d, 0, true); 2175 1, 1, instr.alu.saturate_d, 0, true);
2176 if (instr.generates_cc) {
2177 LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code");
2178 UNREACHABLE();
2179 }
2110 2180
2111 break; 2181 break;
2112 } 2182 }
@@ -2212,6 +2282,11 @@ private:
2212 } 2282 }
2213 2283
2214 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2284 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2285
2286 if (instr.generates_cc) {
2287 LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code");
2288 UNREACHABLE();
2289 }
2215 break; 2290 break;
2216 } 2291 }
2217 case OpCode::Id::F2F_R: { 2292 case OpCode::Id::F2F_R: {
@@ -2250,6 +2325,11 @@ private:
2250 } 2325 }
2251 2326
2252 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); 2327 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
2328
2329 if (instr.generates_cc) {
2330 LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code");
2331 UNREACHABLE();
2332 }
2253 break; 2333 break;
2254 } 2334 }
2255 case OpCode::Id::F2I_R: 2335 case OpCode::Id::F2I_R:
@@ -2299,6 +2379,10 @@ private:
2299 2379
2300 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 2380 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
2301 1, false, 0, instr.conversion.dest_size); 2381 1, false, 0, instr.conversion.dest_size);
2382 if (instr.generates_cc) {
2383 LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code");
2384 UNREACHABLE();
2385 }
2302 break; 2386 break;
2303 } 2387 }
2304 default: { 2388 default: {
@@ -3107,6 +3191,11 @@ private:
3107 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); 3191 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
3108 } 3192 }
3109 3193
3194 if (instr.generates_cc) {
3195 LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
3196 UNREACHABLE();
3197 }
3198
3110 break; 3199 break;
3111 } 3200 }
3112 case OpCode::Type::PredicateSetPredicate: { 3201 case OpCode::Type::PredicateSetPredicate: {
@@ -3372,6 +3461,10 @@ private:
3372 } 3461 }
3373 3462
3374 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); 3463 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
3464 if (instr.generates_cc) {
3465 LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code");
3466 UNREACHABLE();
3467 }
3375 break; 3468 break;
3376 } 3469 }
3377 default: { 3470 default: {
@@ -3381,6 +3474,12 @@ private:
3381 EmitFragmentOutputsWrite(); 3474 EmitFragmentOutputsWrite();
3382 } 3475 }
3383 3476
3477 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3478 if (cc != Tegra::Shader::ControlCode::T) {
3479 LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
3480 UNREACHABLE();
3481 }
3482
3384 switch (instr.flow.cond) { 3483 switch (instr.flow.cond) {
3385 case Tegra::Shader::FlowCondition::Always: 3484 case Tegra::Shader::FlowCondition::Always:
3386 shader.AddLine("return true;"); 3485 shader.AddLine("return true;");
@@ -3410,6 +3509,11 @@ private:
3410 3509
3411 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 3510 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
3412 // about unexecuted instructions that may follow this. 3511 // about unexecuted instructions that may follow this.
3512 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3513 if (cc != Tegra::Shader::ControlCode::T) {
3514 LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
3515 UNREACHABLE();
3516 }
3413 shader.AddLine("if (true) {"); 3517 shader.AddLine("if (true) {");
3414 ++shader.scope; 3518 ++shader.scope;
3415 shader.AddLine("discard;"); 3519 shader.AddLine("discard;");
@@ -3467,6 +3571,11 @@ private:
3467 case OpCode::Id::BRA: { 3571 case OpCode::Id::BRA: {
3468 ASSERT_MSG(instr.bra.constant_buffer == 0, 3572 ASSERT_MSG(instr.bra.constant_buffer == 0,
3469 "BRA with constant buffers are not implemented"); 3573 "BRA with constant buffers are not implemented");
3574 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3575 if (cc != Tegra::Shader::ControlCode::T) {
3576 LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc));
3577 UNREACHABLE();
3578 }
3470 const u32 target = offset + instr.bra.GetBranchTarget(); 3579 const u32 target = offset + instr.bra.GetBranchTarget();
3471 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 3580 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
3472 break; 3581 break;
@@ -3507,13 +3616,21 @@ private:
3507 } 3616 }
3508 case OpCode::Id::SYNC: { 3617 case OpCode::Id::SYNC: {
3509 // The SYNC opcode jumps to the address previously set by the SSY opcode 3618 // The SYNC opcode jumps to the address previously set by the SSY opcode
3510 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3619 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3620 if (cc != Tegra::Shader::ControlCode::T) {
3621 LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
3622 UNREACHABLE();
3623 }
3511 EmitPopFromFlowStack(); 3624 EmitPopFromFlowStack();
3512 break; 3625 break;
3513 } 3626 }
3514 case OpCode::Id::BRK: { 3627 case OpCode::Id::BRK: {
3515 // The BRK opcode jumps to the address previously set by the PBK opcode 3628 // The BRK opcode jumps to the address previously set by the PBK opcode
3516 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3629 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3630 if (cc != Tegra::Shader::ControlCode::T) {
3631 LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc));
3632 UNREACHABLE();
3633 }
3517 EmitPopFromFlowStack(); 3634 EmitPopFromFlowStack();
3518 break; 3635 break;
3519 } 3636 }
@@ -3543,6 +3660,11 @@ private:
3543 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3660 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3544 instr.vmad.saturate == 1, 0, Register::Size::Word, 3661 instr.vmad.saturate == 1, 0, Register::Size::Word,
3545 instr.vmad.cc); 3662 instr.vmad.cc);
3663 if (instr.generates_cc) {
3664 LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code");
3665 UNREACHABLE();
3666 }
3667
3546 break; 3668 break;
3547 } 3669 }
3548 case OpCode::Id::VSETP: { 3670 case OpCode::Id::VSETP: {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index d8a43cc94..b6b426f34 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -89,7 +89,18 @@ OpenGLState::OpenGLState() {
89 point.size = 1; 89 point.size = 1;
90} 90}
91 91
92void OpenGLState::Apply() const { 92void OpenGLState::ApplyDefaultState() {
93 glDisable(GL_FRAMEBUFFER_SRGB);
94 glDisable(GL_CULL_FACE);
95 glDisable(GL_DEPTH_TEST);
96 glDisable(GL_PRIMITIVE_RESTART);
97 glDisable(GL_STENCIL_TEST);
98 glEnable(GL_BLEND);
99 glDisable(GL_COLOR_LOGIC_OP);
100 glDisable(GL_SCISSOR_TEST);
101}
102
103void OpenGLState::ApplySRgb() const {
93 // sRGB 104 // sRGB
94 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { 105 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
95 if (framebuffer_srgb.enabled) { 106 if (framebuffer_srgb.enabled) {
@@ -100,96 +111,122 @@ void OpenGLState::Apply() const {
100 glDisable(GL_FRAMEBUFFER_SRGB); 111 glDisable(GL_FRAMEBUFFER_SRGB);
101 } 112 }
102 } 113 }
114}
115
116void OpenGLState::ApplyCulling() const {
103 // Culling 117 // Culling
104 if (cull.enabled != cur_state.cull.enabled) { 118 const bool cull_changed = cull.enabled != cur_state.cull.enabled;
119 if (cull_changed) {
105 if (cull.enabled) { 120 if (cull.enabled) {
106 glEnable(GL_CULL_FACE); 121 glEnable(GL_CULL_FACE);
107 } else { 122 } else {
108 glDisable(GL_CULL_FACE); 123 glDisable(GL_CULL_FACE);
109 } 124 }
110 } 125 }
126 if (cull.enabled) {
127 if (cull_changed || cull.mode != cur_state.cull.mode) {
128 glCullFace(cull.mode);
129 }
111 130
112 if (cull.mode != cur_state.cull.mode) { 131 if (cull_changed || cull.front_face != cur_state.cull.front_face) {
113 glCullFace(cull.mode); 132 glFrontFace(cull.front_face);
114 } 133 }
115
116 if (cull.front_face != cur_state.cull.front_face) {
117 glFrontFace(cull.front_face);
118 } 134 }
135}
119 136
137void OpenGLState::ApplyDepth() const {
120 // Depth test 138 // Depth test
121 if (depth.test_enabled != cur_state.depth.test_enabled) { 139 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
140 if (depth_test_changed) {
122 if (depth.test_enabled) { 141 if (depth.test_enabled) {
123 glEnable(GL_DEPTH_TEST); 142 glEnable(GL_DEPTH_TEST);
124 } else { 143 } else {
125 glDisable(GL_DEPTH_TEST); 144 glDisable(GL_DEPTH_TEST);
126 } 145 }
127 } 146 }
128 147 if (depth.test_enabled &&
129 if (depth.test_func != cur_state.depth.test_func) { 148 (depth_test_changed || depth.test_func != cur_state.depth.test_func)) {
130 glDepthFunc(depth.test_func); 149 glDepthFunc(depth.test_func);
131 } 150 }
132
133 // Depth mask 151 // Depth mask
134 if (depth.write_mask != cur_state.depth.write_mask) { 152 if (depth.write_mask != cur_state.depth.write_mask) {
135 glDepthMask(depth.write_mask); 153 glDepthMask(depth.write_mask);
136 } 154 }
137
138 // Depth range 155 // Depth range
139 if (depth.depth_range_near != cur_state.depth.depth_range_near || 156 if (depth.depth_range_near != cur_state.depth.depth_range_near ||
140 depth.depth_range_far != cur_state.depth.depth_range_far) { 157 depth.depth_range_far != cur_state.depth.depth_range_far) {
141 glDepthRange(depth.depth_range_near, depth.depth_range_far); 158 glDepthRange(depth.depth_range_near, depth.depth_range_far);
142 } 159 }
160}
143 161
144 // Primitive restart 162void OpenGLState::ApplyPrimitiveRestart() const {
145 if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { 163 const bool primitive_restart_changed =
164 primitive_restart.enabled != cur_state.primitive_restart.enabled;
165 if (primitive_restart_changed) {
146 if (primitive_restart.enabled) { 166 if (primitive_restart.enabled) {
147 glEnable(GL_PRIMITIVE_RESTART); 167 glEnable(GL_PRIMITIVE_RESTART);
148 } else { 168 } else {
149 glDisable(GL_PRIMITIVE_RESTART); 169 glDisable(GL_PRIMITIVE_RESTART);
150 } 170 }
151 } 171 }
152 if (primitive_restart.index != cur_state.primitive_restart.index) { 172 if (primitive_restart_changed ||
173 (primitive_restart.enabled &&
174 primitive_restart.index != cur_state.primitive_restart.index)) {
153 glPrimitiveRestartIndex(primitive_restart.index); 175 glPrimitiveRestartIndex(primitive_restart.index);
154 } 176 }
177}
155 178
156 // Color mask 179void OpenGLState::ApplyStencilTest() const {
157 if (color_mask.red_enabled != cur_state.color_mask.red_enabled || 180 const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled;
158 color_mask.green_enabled != cur_state.color_mask.green_enabled || 181 if (stencil_test_changed) {
159 color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
160 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
161 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
162 color_mask.alpha_enabled);
163 }
164
165 // Stencil test
166 if (stencil.test_enabled != cur_state.stencil.test_enabled) {
167 if (stencil.test_enabled) { 182 if (stencil.test_enabled) {
168 glEnable(GL_STENCIL_TEST); 183 glEnable(GL_STENCIL_TEST);
169 } else { 184 } else {
170 glDisable(GL_STENCIL_TEST); 185 glDisable(GL_STENCIL_TEST);
171 } 186 }
172 } 187 }
173 auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) { 188 if (stencil.test_enabled) {
174 if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || 189 auto config_stencil = [stencil_test_changed](GLenum face, const auto& config,
175 config.test_mask != prev_config.test_mask) { 190 const auto& prev_config) {
176 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); 191 if (stencil_test_changed || config.test_func != prev_config.test_func ||
177 } 192 config.test_ref != prev_config.test_ref ||
178 if (config.action_depth_fail != prev_config.action_depth_fail || 193 config.test_mask != prev_config.test_mask) {
179 config.action_depth_pass != prev_config.action_depth_pass || 194 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
180 config.action_stencil_fail != prev_config.action_stencil_fail) { 195 }
181 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, 196 if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail ||
182 config.action_depth_pass); 197 config.action_depth_pass != prev_config.action_depth_pass ||
183 } 198 config.action_stencil_fail != prev_config.action_stencil_fail) {
184 if (config.write_mask != prev_config.write_mask) { 199 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
185 glStencilMaskSeparate(face, config.write_mask); 200 config.action_depth_pass);
201 }
202 if (config.write_mask != prev_config.write_mask) {
203 glStencilMaskSeparate(face, config.write_mask);
204 }
205 };
206 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
207 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
208 }
209}
210
211void OpenGLState::ApplyScissorTest() const {
212 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
213 if (scissor_changed) {
214 if (scissor.enabled) {
215 glEnable(GL_SCISSOR_TEST);
216 } else {
217 glDisable(GL_SCISSOR_TEST);
186 } 218 }
187 }; 219 }
188 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); 220 if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x ||
189 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); 221 scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width ||
222 scissor.height != cur_state.scissor.height) {
223 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
224 }
225}
190 226
191 // Blending 227void OpenGLState::ApplyBlending() const {
192 if (blend.enabled != cur_state.blend.enabled) { 228 const bool blend_changed = blend.enabled != cur_state.blend.enabled;
229 if (blend_changed) {
193 if (blend.enabled) { 230 if (blend.enabled) {
194 ASSERT(!logic_op.enabled); 231 ASSERT(!logic_op.enabled);
195 glEnable(GL_BLEND); 232 glEnable(GL_BLEND);
@@ -197,29 +234,32 @@ void OpenGLState::Apply() const {
197 glDisable(GL_BLEND); 234 glDisable(GL_BLEND);
198 } 235 }
199 } 236 }
237 if (blend.enabled) {
238 if (blend_changed || blend.color.red != cur_state.blend.color.red ||
239 blend.color.green != cur_state.blend.color.green ||
240 blend.color.blue != cur_state.blend.color.blue ||
241 blend.color.alpha != cur_state.blend.color.alpha) {
242 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
243 }
200 244
201 if (blend.color.red != cur_state.blend.color.red || 245 if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func ||
202 blend.color.green != cur_state.blend.color.green || 246 blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
203 blend.color.blue != cur_state.blend.color.blue || 247 blend.src_a_func != cur_state.blend.src_a_func ||
204 blend.color.alpha != cur_state.blend.color.alpha) { 248 blend.dst_a_func != cur_state.blend.dst_a_func) {
205 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); 249 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
206 } 250 blend.dst_a_func);
207 251 }
208 if (blend.src_rgb_func != cur_state.blend.src_rgb_func ||
209 blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
210 blend.src_a_func != cur_state.blend.src_a_func ||
211 blend.dst_a_func != cur_state.blend.dst_a_func) {
212 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
213 blend.dst_a_func);
214 }
215 252
216 if (blend.rgb_equation != cur_state.blend.rgb_equation || 253 if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation ||
217 blend.a_equation != cur_state.blend.a_equation) { 254 blend.a_equation != cur_state.blend.a_equation) {
218 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); 255 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
256 }
219 } 257 }
258}
220 259
221 // Logic Operation 260void OpenGLState::ApplyLogicOp() const {
222 if (logic_op.enabled != cur_state.logic_op.enabled) { 261 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
262 if (logic_op_changed) {
223 if (logic_op.enabled) { 263 if (logic_op.enabled) {
224 ASSERT(!blend.enabled); 264 ASSERT(!blend.enabled);
225 glEnable(GL_COLOR_LOGIC_OP); 265 glEnable(GL_COLOR_LOGIC_OP);
@@ -228,11 +268,13 @@ void OpenGLState::Apply() const {
228 } 268 }
229 } 269 }
230 270
231 if (logic_op.operation != cur_state.logic_op.operation) { 271 if (logic_op.enabled &&
272 (logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) {
232 glLogicOp(logic_op.operation); 273 glLogicOp(logic_op.operation);
233 } 274 }
275}
234 276
235 // Textures 277void OpenGLState::ApplyTextures() const {
236 for (std::size_t i = 0; i < std::size(texture_units); ++i) { 278 for (std::size_t i = 0; i < std::size(texture_units); ++i) {
237 const auto& texture_unit = texture_units[i]; 279 const auto& texture_unit = texture_units[i];
238 const auto& cur_state_texture_unit = cur_state.texture_units[i]; 280 const auto& cur_state_texture_unit = cur_state.texture_units[i];
@@ -251,28 +293,29 @@ void OpenGLState::Apply() const {
251 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); 293 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
252 } 294 }
253 } 295 }
296}
254 297
255 // Samplers 298void OpenGLState::ApplySamplers() const {
256 { 299 bool has_delta{};
257 bool has_delta{}; 300 std::size_t first{}, last{};
258 std::size_t first{}, last{}; 301 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
259 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; 302 for (std::size_t i = 0; i < std::size(samplers); ++i) {
260 for (std::size_t i = 0; i < std::size(samplers); ++i) { 303 samplers[i] = texture_units[i].sampler;
261 samplers[i] = texture_units[i].sampler; 304 if (samplers[i] != cur_state.texture_units[i].sampler) {
262 if (samplers[i] != cur_state.texture_units[i].sampler) { 305 if (!has_delta) {
263 if (!has_delta) { 306 first = i;
264 first = i; 307 has_delta = true;
265 has_delta = true;
266 }
267 last = i;
268 } 308 }
269 } 309 last = i;
270 if (has_delta) {
271 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
272 samplers.data());
273 } 310 }
274 } 311 }
312 if (has_delta) {
313 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
314 samplers.data());
315 }
316}
275 317
318void OpenGLState::Apply() const {
276 // Framebuffer 319 // Framebuffer
277 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 320 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
278 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 321 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -305,27 +348,12 @@ void OpenGLState::Apply() const {
305 if (draw.program_pipeline != cur_state.draw.program_pipeline) { 348 if (draw.program_pipeline != cur_state.draw.program_pipeline) {
306 glBindProgramPipeline(draw.program_pipeline); 349 glBindProgramPipeline(draw.program_pipeline);
307 } 350 }
308 351 // Viewport
309 // Scissor test
310 if (scissor.enabled != cur_state.scissor.enabled) {
311 if (scissor.enabled) {
312 glEnable(GL_SCISSOR_TEST);
313 } else {
314 glDisable(GL_SCISSOR_TEST);
315 }
316 }
317
318 if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
319 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
320 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
321 }
322
323 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y || 352 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
324 viewport.width != cur_state.viewport.width || 353 viewport.width != cur_state.viewport.width ||
325 viewport.height != cur_state.viewport.height) { 354 viewport.height != cur_state.viewport.height) {
326 glViewport(viewport.x, viewport.y, viewport.width, viewport.height); 355 glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
327 } 356 }
328
329 // Clip distance 357 // Clip distance
330 for (std::size_t i = 0; i < clip_distance.size(); ++i) { 358 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
331 if (clip_distance[i] != cur_state.clip_distance[i]) { 359 if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -336,12 +364,28 @@ void OpenGLState::Apply() const {
336 } 364 }
337 } 365 }
338 } 366 }
339 367 // Color mask
368 if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
369 color_mask.green_enabled != cur_state.color_mask.green_enabled ||
370 color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
371 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
372 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
373 color_mask.alpha_enabled);
374 }
340 // Point 375 // Point
341 if (point.size != cur_state.point.size) { 376 if (point.size != cur_state.point.size) {
342 glPointSize(point.size); 377 glPointSize(point.size);
343 } 378 }
344 379 ApplyScissorTest();
380 ApplyStencilTest();
381 ApplySRgb();
382 ApplyCulling();
383 ApplyDepth();
384 ApplyPrimitiveRestart();
385 ApplyBlending();
386 ApplyLogicOp();
387 ApplyTextures();
388 ApplySamplers();
345 cur_state = *this; 389 cur_state = *this;
346} 390}
347 391
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 9e2c573b5..fe648aff6 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -173,7 +173,8 @@ public:
173 } 173 }
174 /// Apply this state as the current OpenGL state 174 /// Apply this state as the current OpenGL state
175 void Apply() const; 175 void Apply() const;
176 176 /// Set the initial OpenGL state
177 static void ApplyDefaultState();
177 /// Resets any references to the given resource 178 /// Resets any references to the given resource
178 OpenGLState& UnbindTexture(GLuint handle); 179 OpenGLState& UnbindTexture(GLuint handle);
179 OpenGLState& ResetSampler(GLuint handle); 180 OpenGLState& ResetSampler(GLuint handle);
@@ -188,6 +189,16 @@ private:
188 // Workaround for sRGB problems caused by 189 // Workaround for sRGB problems caused by
189 // QT not supporting srgb output 190 // QT not supporting srgb output
190 static bool s_rgb_used; 191 static bool s_rgb_used;
192 void ApplySRgb() const;
193 void ApplyCulling() const;
194 void ApplyDepth() const;
195 void ApplyPrimitiveRestart() const;
196 void ApplyStencilTest() const;
197 void ApplyScissorTest() const;
198 void ApplyBlending() const;
199 void ApplyLogicOp() const;
200 void ApplyTextures() const;
201 void ApplySamplers() const;
191}; 202};
192 203
193} // namespace OpenGL 204} // namespace OpenGL
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index e6941b95f..051ad3964 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -300,6 +300,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
300 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4; 300 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
301 case Tegra::Texture::TextureFormat::ASTC_2D_5X4: 301 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
302 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4; 302 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
303 case Tegra::Texture::TextureFormat::ASTC_2D_5X5:
304 return is_srgb ? PixelFormat::ASTC_2D_5X5_SRGB : PixelFormat::ASTC_2D_5X5;
303 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 305 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
304 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; 306 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
305 case Tegra::Texture::TextureFormat::ASTC_2D_8X5: 307 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
@@ -443,10 +445,12 @@ bool IsPixelFormatASTC(PixelFormat format) {
443 switch (format) { 445 switch (format) {
444 case PixelFormat::ASTC_2D_4X4: 446 case PixelFormat::ASTC_2D_4X4:
445 case PixelFormat::ASTC_2D_5X4: 447 case PixelFormat::ASTC_2D_5X4:
448 case PixelFormat::ASTC_2D_5X5:
446 case PixelFormat::ASTC_2D_8X8: 449 case PixelFormat::ASTC_2D_8X8:
447 case PixelFormat::ASTC_2D_8X5: 450 case PixelFormat::ASTC_2D_8X5:
448 case PixelFormat::ASTC_2D_4X4_SRGB: 451 case PixelFormat::ASTC_2D_4X4_SRGB:
449 case PixelFormat::ASTC_2D_5X4_SRGB: 452 case PixelFormat::ASTC_2D_5X4_SRGB:
453 case PixelFormat::ASTC_2D_5X5_SRGB:
450 case PixelFormat::ASTC_2D_8X8_SRGB: 454 case PixelFormat::ASTC_2D_8X8_SRGB:
451 case PixelFormat::ASTC_2D_8X5_SRGB: 455 case PixelFormat::ASTC_2D_8X5_SRGB:
452 return true; 456 return true;
@@ -456,27 +460,7 @@ bool IsPixelFormatASTC(PixelFormat format) {
456} 460}
457 461
458std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { 462std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
459 switch (format) { 463 return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(format)};
460 case PixelFormat::ASTC_2D_4X4:
461 return {4, 4};
462 case PixelFormat::ASTC_2D_5X4:
463 return {5, 4};
464 case PixelFormat::ASTC_2D_8X8:
465 return {8, 8};
466 case PixelFormat::ASTC_2D_8X5:
467 return {8, 5};
468 case PixelFormat::ASTC_2D_4X4_SRGB:
469 return {4, 4};
470 case PixelFormat::ASTC_2D_5X4_SRGB:
471 return {5, 4};
472 case PixelFormat::ASTC_2D_8X8_SRGB:
473 return {8, 8};
474 case PixelFormat::ASTC_2D_8X5_SRGB:
475 return {8, 5};
476 default:
477 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
478 UNREACHABLE();
479 }
480} 464}
481 465
482bool IsFormatBCn(PixelFormat format) { 466bool IsFormatBCn(PixelFormat format) {
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 25300a193..dfdb8d122 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -72,19 +72,21 @@ enum class PixelFormat {
72 ASTC_2D_8X8_SRGB = 54, 72 ASTC_2D_8X8_SRGB = 54,
73 ASTC_2D_8X5_SRGB = 55, 73 ASTC_2D_8X5_SRGB = 55,
74 ASTC_2D_5X4_SRGB = 56, 74 ASTC_2D_5X4_SRGB = 56,
75 ASTC_2D_5X5 = 57,
76 ASTC_2D_5X5_SRGB = 58,
75 77
76 MaxColorFormat, 78 MaxColorFormat,
77 79
78 // Depth formats 80 // Depth formats
79 Z32F = 57, 81 Z32F = 59,
80 Z16 = 58, 82 Z16 = 60,
81 83
82 MaxDepthFormat, 84 MaxDepthFormat,
83 85
84 // DepthStencil formats 86 // DepthStencil formats
85 Z24S8 = 59, 87 Z24S8 = 61,
86 S8Z24 = 60, 88 S8Z24 = 62,
87 Z32FS8 = 61, 89 Z32FS8 = 63,
88 90
89 MaxDepthStencilFormat, 91 MaxDepthStencilFormat,
90 92
@@ -189,6 +191,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
189 4, // ASTC_2D_8X8_SRGB 191 4, // ASTC_2D_8X8_SRGB
190 4, // ASTC_2D_8X5_SRGB 192 4, // ASTC_2D_8X5_SRGB
191 4, // ASTC_2D_5X4_SRGB 193 4, // ASTC_2D_5X4_SRGB
194 4, // ASTC_2D_5X5
195 4, // ASTC_2D_5X5_SRGB
192 1, // Z32F 196 1, // Z32F
193 1, // Z16 197 1, // Z16
194 1, // Z24S8 198 1, // Z24S8
@@ -200,6 +204,79 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
200 return compression_factor_table[static_cast<std::size_t>(format)]; 204 return compression_factor_table[static_cast<std::size_t>(format)];
201} 205}
202 206
207static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
208 if (format == PixelFormat::Invalid)
209 return 0;
210 constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
211 1, // ABGR8U
212 1, // ABGR8S
213 1, // ABGR8UI
214 1, // B5G6R5U
215 1, // A2B10G10R10U
216 1, // A1B5G5R5U
217 1, // R8U
218 1, // R8UI
219 1, // RGBA16F
220 1, // RGBA16U
221 1, // RGBA16UI
222 1, // R11FG11FB10F
223 1, // RGBA32UI
224 4, // DXT1
225 4, // DXT23
226 4, // DXT45
227 4, // DXN1
228 4, // DXN2UNORM
229 4, // DXN2SNORM
230 4, // BC7U
231 4, // BC6H_UF16
232 4, // BC6H_SF16
233 4, // ASTC_2D_4X4
234 1, // G8R8U
235 1, // G8R8S
236 1, // BGRA8
237 1, // RGBA32F
238 1, // RG32F
239 1, // R32F
240 1, // R16F
241 1, // R16U
242 1, // R16S
243 1, // R16UI
244 1, // R16I
245 1, // RG16
246 1, // RG16F
247 1, // RG16UI
248 1, // RG16I
249 1, // RG16S
250 1, // RGB32F
251 1, // RGBA8_SRGB
252 1, // RG8U
253 1, // RG8S
254 1, // RG32UI
255 1, // R32UI
256 8, // ASTC_2D_8X8
257 8, // ASTC_2D_8X5
258 5, // ASTC_2D_5X4
259 1, // BGRA8_SRGB
260 4, // DXT1_SRGB
261 4, // DXT23_SRGB
262 4, // DXT45_SRGB
263 4, // BC7U_SRGB
264 4, // ASTC_2D_4X4_SRGB
265 8, // ASTC_2D_8X8_SRGB
266 8, // ASTC_2D_8X5_SRGB
267 5, // ASTC_2D_5X4_SRGB
268 5, // ASTC_2D_5X5
269 5, // ASTC_2D_5X5_SRGB
270 1, // Z32F
271 1, // Z16
272 1, // Z24S8
273 1, // S8Z24
274 1, // Z32FS8
275 }};
276 ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
277 return block_width_table[static_cast<std::size_t>(format)];
278}
279
203static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { 280static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
204 if (format == PixelFormat::Invalid) 281 if (format == PixelFormat::Invalid)
205 return 0; 282 return 0;
@@ -262,6 +339,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
262 8, // ASTC_2D_8X8_SRGB 339 8, // ASTC_2D_8X8_SRGB
263 5, // ASTC_2D_8X5_SRGB 340 5, // ASTC_2D_8X5_SRGB
264 4, // ASTC_2D_5X4_SRGB 341 4, // ASTC_2D_5X4_SRGB
342 5, // ASTC_2D_5X5
343 5, // ASTC_2D_5X5_SRGB
265 1, // Z32F 344 1, // Z32F
266 1, // Z16 345 1, // Z16
267 1, // Z24S8 346 1, // Z24S8
@@ -300,7 +379,7 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
300 128, // BC7U 379 128, // BC7U
301 128, // BC6H_UF16 380 128, // BC6H_UF16
302 128, // BC6H_SF16 381 128, // BC6H_SF16
303 32, // ASTC_2D_4X4 382 128, // ASTC_2D_4X4
304 16, // G8R8U 383 16, // G8R8U
305 16, // G8R8S 384 16, // G8R8S
306 32, // BGRA8 385 32, // BGRA8
@@ -323,18 +402,20 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
323 16, // RG8S 402 16, // RG8S
324 64, // RG32UI 403 64, // RG32UI
325 32, // R32UI 404 32, // R32UI
326 16, // ASTC_2D_8X8 405 128, // ASTC_2D_8X8
327 16, // ASTC_2D_8X5 406 128, // ASTC_2D_8X5
328 32, // ASTC_2D_5X4 407 128, // ASTC_2D_5X4
329 32, // BGRA8_SRGB 408 32, // BGRA8_SRGB
330 64, // DXT1_SRGB 409 64, // DXT1_SRGB
331 128, // DXT23_SRGB 410 128, // DXT23_SRGB
332 128, // DXT45_SRGB 411 128, // DXT45_SRGB
333 128, // BC7U 412 128, // BC7U
334 32, // ASTC_2D_4X4_SRGB 413 128, // ASTC_2D_4X4_SRGB
335 16, // ASTC_2D_8X8_SRGB 414 128, // ASTC_2D_8X8_SRGB
336 16, // ASTC_2D_8X5_SRGB 415 128, // ASTC_2D_8X5_SRGB
337 32, // ASTC_2D_5X4_SRGB 416 128, // ASTC_2D_5X4_SRGB
417 128, // ASTC_2D_5X5
418 128, // ASTC_2D_5X5_SRGB
338 32, // Z32F 419 32, // Z32F
339 16, // Z16 420 16, // Z16
340 32, // Z24S8 421 32, // Z24S8
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index b1feacae9..bc50a4876 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1598,27 +1598,29 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1598namespace Tegra::Texture::ASTC { 1598namespace Tegra::Texture::ASTC {
1599 1599
1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
1601 uint32_t block_width, uint32_t block_height) { 1601 uint32_t depth, uint32_t block_width, uint32_t block_height) {
1602 uint32_t blockIdx = 0; 1602 uint32_t blockIdx = 0;
1603 std::vector<uint8_t> outData(height * width * 4); 1603 std::vector<uint8_t> outData(height * width * depth * 4);
1604 for (uint32_t j = 0; j < height; j += block_height) { 1604 for (uint32_t k = 0; k < depth; k++) {
1605 for (uint32_t i = 0; i < width; i += block_width) { 1605 for (uint32_t j = 0; j < height; j += block_height) {
1606 for (uint32_t i = 0; i < width; i += block_width) {
1606 1607
1607 uint8_t* blockPtr = data.data() + blockIdx * 16; 1608 uint8_t* blockPtr = data.data() + blockIdx * 16;
1608 1609
1609 // Blocks can be at most 12x12 1610 // Blocks can be at most 12x12
1610 uint32_t uncompData[144]; 1611 uint32_t uncompData[144];
1611 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData); 1612 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
1612 1613
1613 uint32_t decompWidth = std::min(block_width, width - i); 1614 uint32_t decompWidth = std::min(block_width, width - i);
1614 uint32_t decompHeight = std::min(block_height, height - j); 1615 uint32_t decompHeight = std::min(block_height, height - j);
1615 1616
1616 uint8_t* outRow = outData.data() + (j * width + i) * 4; 1617 uint8_t* outRow = outData.data() + (j * width + i) * 4;
1617 for (uint32_t jj = 0; jj < decompHeight; jj++) { 1618 for (uint32_t jj = 0; jj < decompHeight; jj++) {
1618 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); 1619 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
1619 } 1620 }
1620 1621
1621 blockIdx++; 1622 blockIdx++;
1623 }
1622 } 1624 }
1623 } 1625 }
1624 1626
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index f0d7c0e56..d419dd025 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -10,6 +10,6 @@
10namespace Tegra::Texture::ASTC { 10namespace Tegra::Texture::ASTC {
11 11
12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
13 uint32_t block_width, uint32_t block_height); 13 uint32_t depth, uint32_t block_width, uint32_t block_height);
14 14
15} // namespace Tegra::Texture::ASTC 15} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 550ca856c..3066abf61 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -227,12 +227,14 @@ u32 BytesPerPixel(TextureFormat format) {
227 } 227 }
228} 228}
229 229
230std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 230std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
231 u32 height, u32 depth, u32 block_height, u32 block_depth) { 231 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
232 u32 block_height, u32 block_depth) {
232 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
233 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel, 234 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
234 Memory::GetPointer(address), unswizzled_data.data(), true, block_height, 235 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
235 block_depth); 236 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
237 block_height, block_depth);
236 return unswizzled_data; 238 return unswizzled_data;
237} 239}
238 240
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index b390219e4..ba065510b 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -19,8 +19,8 @@ inline std::size_t GetGOBSize() {
19/** 19/**
20 * Unswizzles a swizzled texture without changing its format. 20 * Unswizzles a swizzled texture without changing its format.
21 */ 21 */
22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 height, u32 depth, 23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight, 24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight); 25 u32 block_depth = TICEntry::DefaultBlockHeight);
26 26
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 0a8f2bd9e..9156ce802 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -102,16 +102,27 @@ void TelemetryJson::Complete() {
102 impl->SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
103 impl->SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
105 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
106 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 105 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
107 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 106 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
108 107
109 auto content = impl->TopSection().dump(); 108 auto content = impl->TopSection().dump();
110 // Send the telemetry async but don't handle the errors since they were written to the log 109 // Send the telemetry async but don't handle the errors since they were written to the log
111 Common::DetachedTasks::AddTask( 110 Common::DetachedTasks::AddTask([host{impl->host}, content]() {
112 [host{impl->host}, username{impl->username}, token{impl->token}, content]() { 111 Client{host, "", ""}.PostJson("/telemetry", content, true);
113 Client{host, username, token}.PostJson("/telemetry", content, true); 112 });
114 }); 113}
114
115bool TelemetryJson::SubmitTestcase() {
116 impl->SerializeSection(Telemetry::FieldType::App, "App");
117 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
118 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
119 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
120
121 auto content = impl->TopSection().dump();
122 Client client(impl->host, impl->username, impl->token);
123 auto value = client.PostJson("/gamedb/testcase", content, false);
124
125 return value.result_code == Common::WebResult::Code::Success;
115} 126}
116 127
117} // namespace WebService 128} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 93371414a..dfd202829 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -35,6 +35,7 @@ public:
35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; 35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
36 36
37 void Complete() override; 37 void Complete() override;
38 bool SubmitTestcase() override;
38 39
39private: 40private:
40 struct Impl; 41 struct Impl;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 9379d9110..f9ca2948e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -56,6 +56,8 @@ add_executable(yuzu
56 main.h 56 main.h
57 ui_settings.cpp 57 ui_settings.cpp
58 ui_settings.h 58 ui_settings.h
59 util/limitable_input_dialog.cpp
60 util/limitable_input_dialog.h
59 util/spinbox.cpp 61 util/spinbox.cpp
60 util/spinbox.h 62 util/spinbox.h
61 util/util.cpp 63 util/util.cpp
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 91e754274..5f0896f84 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -5,6 +5,7 @@
5#include <QButtonGroup> 5#include <QButtonGroup>
6#include <QMessageBox> 6#include <QMessageBox>
7#include <QPushButton> 7#include <QPushButton>
8#include <QtConcurrent/qtconcurrentrun.h>
8#include "common/logging/log.h" 9#include "common/logging/log.h"
9#include "common/telemetry.h" 10#include "common/telemetry.h"
10#include "core/core.h" 11#include "core/core.h"
@@ -23,6 +24,8 @@ CompatDB::CompatDB(QWidget* parent)
23 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); 24 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
24 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); 25 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
25 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); 26 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
27 connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
28 &CompatDB::OnTestcaseSubmitted);
26} 29}
27 30
28CompatDB::~CompatDB() = default; 31CompatDB::~CompatDB() = default;
@@ -48,18 +51,38 @@ void CompatDB::Submit() {
48 } 51 }
49 break; 52 break;
50 case CompatDBPage::Final: 53 case CompatDBPage::Final:
54 back();
51 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
52 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", 56 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
53 compatibility->checkedId()); 57 compatibility->checkedId());
54 // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a 58
55 // workaround 59 button(NextButton)->setEnabled(false);
60 button(NextButton)->setText(tr("Submitting"));
56 button(QWizard::CancelButton)->setVisible(false); 61 button(QWizard::CancelButton)->setVisible(false);
62
63 testcase_watcher.setFuture(QtConcurrent::run(
64 [this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
57 break; 65 break;
58 default: 66 default:
59 LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); 67 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
60 } 68 }
61} 69}
62 70
71void CompatDB::OnTestcaseSubmitted() {
72 if (!testcase_watcher.result()) {
73 QMessageBox::critical(this, tr("Communication error"),
74 tr("An error occured while sending the Testcase"));
75 button(NextButton)->setEnabled(true);
76 button(NextButton)->setText(tr("Next"));
77 button(QWizard::CancelButton)->setVisible(true);
78 } else {
79 next();
80 // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
81 // workaround
82 button(QWizard::CancelButton)->setVisible(false);
83 }
84}
85
63void CompatDB::EnableNext() { 86void CompatDB::EnableNext() {
64 button(NextButton)->setEnabled(true); 87 button(NextButton)->setEnabled(true);
65} 88}
diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h
index ca0dd11d6..5381f67f7 100644
--- a/src/yuzu/compatdb.h
+++ b/src/yuzu/compatdb.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <QFutureWatcher>
8#include <QWizard> 9#include <QWizard>
9 10
10namespace Ui { 11namespace Ui {
@@ -19,8 +20,11 @@ public:
19 ~CompatDB(); 20 ~CompatDB();
20 21
21private: 22private:
23 QFutureWatcher<bool> testcase_watcher;
24
22 std::unique_ptr<Ui::CompatDB> ui; 25 std::unique_ptr<Ui::CompatDB> ui;
23 26
24 void Submit(); 27 void Submit();
28 void OnTestcaseSubmitted();
25 void EnableNext(); 29 void EnableNext();
26}; 30};
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 1b8aa7de2..b4b4a4a56 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -6,20 +6,20 @@
6#include <QFileDialog> 6#include <QFileDialog>
7#include <QGraphicsItem> 7#include <QGraphicsItem>
8#include <QGraphicsScene> 8#include <QGraphicsScene>
9#include <QInputDialog> 9#include <QHeaderView>
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QStandardItemModel> 11#include <QStandardItemModel>
12#include <QTreeView> 12#include <QTreeView>
13#include <QVBoxLayout> 13#include <QVBoxLayout>
14#include "common/common_paths.h" 14#include "common/assert.h"
15#include "common/logging/backend.h" 15#include "common/file_util.h"
16#include "common/string_util.h" 16#include "common/string_util.h"
17#include "core/core.h" 17#include "core/core.h"
18#include "core/hle/service/acc/profile_manager.h" 18#include "core/hle/service/acc/profile_manager.h"
19#include "core/settings.h" 19#include "core/settings.h"
20#include "ui_configure_system.h" 20#include "ui_configure_system.h"
21#include "yuzu/configuration/configure_system.h" 21#include "yuzu/configuration/configure_system.h"
22#include "yuzu/main.h" 22#include "yuzu/util/limitable_input_dialog.h"
23 23
24namespace { 24namespace {
25constexpr std::array<int, 12> days_in_month = {{ 25constexpr std::array<int, 12> days_in_month = {{
@@ -83,6 +83,12 @@ QPixmap GetIcon(Service::Account::UUID uuid) {
83 83
84 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 84 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
85} 85}
86
87QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) {
88 return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"),
89 description_text, 1,
90 static_cast<int>(Service::Account::profile_username_size));
91}
86} // Anonymous namespace 92} // Anonymous namespace
87 93
88ConfigureSystem::ConfigureSystem(QWidget* parent) 94ConfigureSystem::ConfigureSystem(QWidget* parent)
@@ -244,15 +250,13 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) {
244} 250}
245 251
246void ConfigureSystem::AddUser() { 252void ConfigureSystem::AddUser() {
247 const auto uuid = Service::Account::UUID::Generate();
248
249 bool ok = false;
250 const auto username = 253 const auto username =
251 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"), 254 GetProfileUsernameFromUser(this, tr("Enter a username for the new user:"));
252 QLineEdit::Normal, QString(), &ok); 255 if (username.isEmpty()) {
253 if (!ok)
254 return; 256 return;
257 }
255 258
259 const auto uuid = Service::Account::UUID::Generate();
256 profile_manager->CreateNewUser(uuid, username.toStdString()); 260 profile_manager->CreateNewUser(uuid, username.toStdString());
257 261
258 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); 262 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
@@ -267,23 +271,14 @@ void ConfigureSystem::RenameUser() {
267 if (!profile_manager->GetProfileBase(*uuid, profile)) 271 if (!profile_manager->GetProfileBase(*uuid, profile))
268 return; 272 return;
269 273
270 bool ok = false; 274 const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
271 const auto old_username = GetAccountUsername(*profile_manager, *uuid); 275 if (new_username.isEmpty()) {
272 const auto new_username =
273 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"),
274 QLineEdit::Normal, old_username, &ok);
275
276 if (!ok)
277 return; 276 return;
277 }
278 278
279 std::fill(profile.username.begin(), profile.username.end(), '\0');
280 const auto username_std = new_username.toStdString(); 279 const auto username_std = new_username.toStdString();
281 if (username_std.size() > profile.username.size()) { 280 std::fill(profile.username.begin(), profile.username.end(), '\0');
282 std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()), 281 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
283 profile.username.begin());
284 } else {
285 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
286 }
287 282
288 profile_manager->SetProfileBase(*uuid, profile); 283 profile_manager->SetProfileBase(*uuid, profile);
289 284
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 0adbab27d..707747422 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,9 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = 389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format), 390 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
391 surface_width, surface_height, 1U); 391 surface_height, 1U);
392 392
393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
394 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c5a56cbfd..74a44be37 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -142,6 +142,9 @@ static void InitializeLogging() {
142 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 142 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
143 FileUtil::CreateFullPath(log_dir); 143 FileUtil::CreateFullPath(log_dir);
144 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 144 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
145#ifdef _WIN32
146 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
147#endif
145} 148}
146 149
147GMainWindow::GMainWindow() 150GMainWindow::GMainWindow()
@@ -454,6 +457,7 @@ void GMainWindow::ConnectMenuEvents() {
454 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 457 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
455 458
456 // Help 459 // Help
460 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
457 connect(ui.action_Rederive, &QAction::triggered, this, 461 connect(ui.action_Rederive, &QAction::triggered, this,
458 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); 462 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
459 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 463 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
@@ -1374,6 +1378,11 @@ void GMainWindow::OnLoadAmiibo() {
1374 } 1378 }
1375} 1379}
1376 1380
1381void GMainWindow::OnOpenYuzuFolder() {
1382 QDesktopServices::openUrl(QUrl::fromLocalFile(
1383 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
1384}
1385
1377void GMainWindow::OnAbout() { 1386void GMainWindow::OnAbout() {
1378 AboutDialog aboutDialog(this); 1387 AboutDialog aboutDialog(this);
1379 aboutDialog.exec(); 1388 aboutDialog.exec();
@@ -1532,7 +1541,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1532 "derivation. It will be attempted but may not complete.<br><br>") + 1541 "derivation. It will be attempted but may not complete.<br><br>") +
1533 errors + 1542 errors +
1534 tr("<br><br>You can get all of these and dump all of your games easily by " 1543 tr("<br><br>You can get all of these and dump all of your games easily by "
1535 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the " 1544 "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
1536 "quickstart guide</a>. Alternatively, you can use another method of dumping " 1545 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1537 "to obtain all of your keys.")); 1546 "to obtain all of your keys."));
1538 } 1547 }
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index af637d89e..929250e8c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -167,6 +167,7 @@ private slots:
167 void OnMenuRecentFile(); 167 void OnMenuRecentFile();
168 void OnConfigure(); 168 void OnConfigure();
169 void OnLoadAmiibo(); 169 void OnLoadAmiibo();
170 void OnOpenYuzuFolder();
170 void OnAbout(); 171 void OnAbout();
171 void OnToggleFilterBar(); 172 void OnToggleFilterBar();
172 void OnDisplayTitleBars(bool); 173 void OnDisplayTitleBars(bool);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 48d099591..28cf269e7 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -110,6 +110,7 @@
110 <string>&amp;Help</string> 110 <string>&amp;Help</string>
111 </property> 111 </property>
112 <addaction name="action_Report_Compatibility"/> 112 <addaction name="action_Report_Compatibility"/>
113 <addaction name="action_Open_yuzu_Folder" />
113 <addaction name="separator"/> 114 <addaction name="separator"/>
114 <addaction name="action_About"/> 115 <addaction name="action_About"/>
115 </widget> 116 </widget>
@@ -277,6 +278,11 @@
277 <bool>false</bool> 278 <bool>false</bool>
278 </property> 279 </property>
279 </action> 280 </action>
281 <action name="action_Open_yuzu_Folder">
282 <property name="text">
283 <string>Open yuzu Folder</string>
284 </property>
285 </action>
280 </widget> 286 </widget>
281 <resources/> 287 <resources/>
282 <connections/> 288 <connections/>
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
new file mode 100644
index 000000000..edd78e579
--- /dev/null
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -0,0 +1,59 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QDialogButtonBox>
6#include <QLabel>
7#include <QLineEdit>
8#include <QPushButton>
9#include <QVBoxLayout>
10#include "yuzu/util/limitable_input_dialog.h"
11
12LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
13 CreateUI();
14 ConnectEvents();
15}
16
17LimitableInputDialog::~LimitableInputDialog() = default;
18
19void LimitableInputDialog::CreateUI() {
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21
22 text_label = new QLabel(this);
23 text_entry = new QLineEdit(this);
24 buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
25
26 auto* const layout = new QVBoxLayout;
27 layout->addWidget(text_label);
28 layout->addWidget(text_entry);
29 layout->addWidget(buttons);
30
31 setLayout(layout);
32}
33
34void LimitableInputDialog::ConnectEvents() {
35 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
36 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
37}
38
39QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text,
40 int min_character_limit, int max_character_limit) {
41 Q_ASSERT(min_character_limit <= max_character_limit);
42
43 LimitableInputDialog dialog{parent};
44 dialog.setWindowTitle(title);
45 dialog.text_label->setText(text);
46 dialog.text_entry->setMaxLength(max_character_limit);
47
48 auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok);
49 ok_button->setEnabled(false);
50 connect(dialog.text_entry, &QLineEdit::textEdited, [&](const QString& new_text) {
51 ok_button->setEnabled(new_text.length() >= min_character_limit);
52 });
53
54 if (dialog.exec() != QDialog::Accepted) {
55 return {};
56 }
57
58 return dialog.text_entry->text();
59}
diff --git a/src/yuzu/util/limitable_input_dialog.h b/src/yuzu/util/limitable_input_dialog.h
new file mode 100644
index 000000000..164ad7301
--- /dev/null
+++ b/src/yuzu/util/limitable_input_dialog.h
@@ -0,0 +1,31 @@
1// Copyright 2018 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 <QDialog>
8
9class QDialogButtonBox;
10class QLabel;
11class QLineEdit;
12
13/// A QDialog that functions similarly to QInputDialog, however, it allows
14/// restricting the minimum and total number of characters that can be entered.
15class LimitableInputDialog final : public QDialog {
16 Q_OBJECT
17public:
18 explicit LimitableInputDialog(QWidget* parent = nullptr);
19 ~LimitableInputDialog() override;
20
21 static QString GetText(QWidget* parent, const QString& title, const QString& text,
22 int min_character_limit, int max_character_limit);
23
24private:
25 void CreateUI();
26 void ConnectEvents();
27
28 QLabel* text_label;
29 QLineEdit* text_entry;
30 QDialogButtonBox* buttons;
31};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c8b93b85b..806127b12 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -76,6 +76,9 @@ static void InitializeLogging() {
76 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 76 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
77 FileUtil::CreateFullPath(log_dir); 77 FileUtil::CreateFullPath(log_dir);
78 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 78 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
79#ifdef _WIN32
80 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
81#endif
79} 82}
80 83
81/// Application entry point 84/// Application entry point