summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/string_util.cpp2
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp4
-rw-r--r--src/core/file_sys/disk_filesystem.cpp6
-rw-r--r--src/core/gdbstub/gdbstub.cpp194
-rw-r--r--src/core/gdbstub/gdbstub.h8
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp4
-rw-r--r--src/core/hle/service/audio/audout_u.cpp24
-rw-r--r--src/core/hle/service/audio/audout_u.h4
-rw-r--r--src/core/hle/service/audio/audren_u.cpp6
-rw-r--r--src/core/hle/service/nifm/nifm.cpp24
-rw-r--r--src/core/hle/service/sockets/bsd.cpp3
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp3
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/loader/nro.cpp4
-rw-r--r--src/core/loader/nso.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp21
-rw-r--r--src/video_core/engines/maxwell_3d.h11
-rw-r--r--src/video_core/engines/shader_bytecode.h21
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp33
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h21
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp138
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp44
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h33
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp15
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h7
-rw-r--r--src/video_core/textures/decoders.cpp6
-rw-r--r--src/yuzu/bootmanager.cpp5
-rw-r--r--src/yuzu/main.cpp3
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp6
-rw-r--r--src/yuzu_cmd/yuzu.cpp9
32 files changed, 545 insertions, 197 deletions
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index ea9d8f77c..0027888c7 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -134,7 +134,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
134 size_t dir_end = full_path.find_last_of("/" 134 size_t dir_end = full_path.find_last_of("/"
135// windows needs the : included for something like just "C:" to be considered a directory 135// windows needs the : included for something like just "C:" to be considered a directory
136#ifdef _WIN32 136#ifdef _WIN32
137 ":" 137 "\\:"
138#endif 138#endif
139 ); 139 );
140 if (std::string::npos == dir_end) 140 if (std::string::npos == dir_end)
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index ce6c5616d..f239cf0ea 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -193,11 +193,11 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
193 } 193 }
194 Kernel::Thread* thread = Kernel::GetCurrentThread(); 194 Kernel::Thread* thread = Kernel::GetCurrentThread();
195 SaveContext(thread->context); 195 SaveContext(thread->context);
196 if (last_bkpt_hit) { 196 if (last_bkpt_hit || (num_instructions == 1)) {
197 last_bkpt_hit = false; 197 last_bkpt_hit = false;
198 GDBStub::Break(); 198 GDBStub::Break();
199 GDBStub::SendTrap(thread, 5);
199 } 200 }
200 GDBStub::SendTrap(thread, 5);
201 } 201 }
202} 202}
203 203
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp
index 8c6f15bb5..d248c2df4 100644
--- a/src/core/file_sys/disk_filesystem.cpp
+++ b/src/core/file_sys/disk_filesystem.cpp
@@ -58,11 +58,13 @@ ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::
58} 58}
59 59
60ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { 60ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const {
61 if (!FileUtil::Exists(path)) { 61 std::string full_path = base_directory + path;
62
63 if (!FileUtil::Exists(full_path)) {
62 return ERROR_PATH_NOT_FOUND; 64 return ERROR_PATH_NOT_FOUND;
63 } 65 }
64 66
65 FileUtil::Delete(path); 67 FileUtil::Delete(full_path);
66 68
67 return RESULT_SUCCESS; 69 return RESULT_SUCCESS;
68} 70}
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 938852a1a..6062de13c 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -61,10 +61,16 @@ const u32 SIGTERM = 15;
61const u32 MSG_WAITALL = 8; 61const u32 MSG_WAITALL = 8;
62#endif 62#endif
63 63
64const u32 X30_REGISTER = 30; 64const u32 LR_REGISTER = 30;
65const u32 SP_REGISTER = 31; 65const u32 SP_REGISTER = 31;
66const u32 PC_REGISTER = 32; 66const u32 PC_REGISTER = 32;
67const u32 CPSR_REGISTER = 33; 67const u32 CPSR_REGISTER = 33;
68const u32 UC_ARM64_REG_Q0 = 34;
69const u32 FPSCR_REGISTER = 66;
70
71// TODO/WiP - Used while working on support for FPU
72const u32 TODO_DUMMY_REG_997 = 997;
73const u32 TODO_DUMMY_REG_998 = 998;
68 74
69// For sample XML files see the GDB source /gdb/features 75// For sample XML files see the GDB source /gdb/features
70// GDB also wants the l character at the start 76// GDB also wants the l character at the start
@@ -130,6 +136,8 @@ static const char* target_xml =
130 </flags> 136 </flags>
131 <reg name="cpsr" bitsize="32" type="cpsr_flags"/> 137 <reg name="cpsr" bitsize="32" type="cpsr_flags"/>
132 </feature> 138 </feature>
139 <feature name="org.gnu.gdb.aarch64.fpu">
140 </feature>
133</target> 141</target>
134)"; 142)";
135 143
@@ -144,6 +152,7 @@ static u32 latest_signal = 0;
144static bool memory_break = false; 152static bool memory_break = false;
145 153
146static Kernel::Thread* current_thread = nullptr; 154static Kernel::Thread* current_thread = nullptr;
155static u32 current_core = 0;
147 156
148// Binding to a port within the reserved ports range (0-1023) requires root permissions, 157// Binding to a port within the reserved ports range (0-1023) requires root permissions,
149// so default to a port outside of that range. 158// so default to a port outside of that range.
@@ -171,13 +180,34 @@ static std::map<u64, Breakpoint> breakpoints_execute;
171static std::map<u64, Breakpoint> breakpoints_read; 180static std::map<u64, Breakpoint> breakpoints_read;
172static std::map<u64, Breakpoint> breakpoints_write; 181static std::map<u64, Breakpoint> breakpoints_write;
173 182
183struct Module {
184 std::string name;
185 PAddr beg;
186 PAddr end;
187};
188
189static std::vector<Module> modules;
190
191void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
192 Module module;
193 if (add_elf_ext) {
194 Common::SplitPath(name, nullptr, &module.name, nullptr);
195 module.name += ".elf";
196 } else {
197 module.name = std::move(name);
198 }
199 module.beg = beg;
200 module.end = end;
201 modules.push_back(std::move(module));
202}
203
174static Kernel::Thread* FindThreadById(int id) { 204static Kernel::Thread* FindThreadById(int id) {
175 for (int core = 0; core < Core::NUM_CPU_CORES; core++) { 205 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
176 auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 206 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
177 for (auto thread : threads) { 207 for (auto& thread : threads) {
178 if (thread->GetThreadId() == id) { 208 if (thread->GetThreadId() == id) {
179 current_thread = thread.get(); 209 current_core = core;
180 return current_thread; 210 return thread.get();
181 } 211 }
182 } 212 }
183 } 213 }
@@ -197,6 +227,8 @@ static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
197 return thread->context.pc; 227 return thread->context.pc;
198 } else if (id == CPSR_REGISTER) { 228 } else if (id == CPSR_REGISTER) {
199 return thread->context.cpsr; 229 return thread->context.cpsr;
230 } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
231 return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0];
200 } else { 232 } else {
201 return 0; 233 return 0;
202 } 234 }
@@ -215,6 +247,8 @@ static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
215 thread->context.pc = val; 247 thread->context.pc = val;
216 } else if (id == CPSR_REGISTER) { 248 } else if (id == CPSR_REGISTER) {
217 thread->context.cpsr = val; 249 thread->context.cpsr = val;
250 } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
251 thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val;
218 } 252 }
219} 253}
220 254
@@ -534,7 +568,11 @@ static void HandleQuery() {
534 SendReply("T0"); 568 SendReply("T0");
535 } else if (strncmp(query, "Supported", strlen("Supported")) == 0) { 569 } else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
536 // PacketSize needs to be large enough for target xml 570 // PacketSize needs to be large enough for target xml
537 SendReply("PacketSize=2000;qXfer:features:read+"); 571 std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+";
572 if (!modules.empty()) {
573 buffer += ";qXfer:libraries:read+";
574 }
575 SendReply(buffer.c_str());
538 } else if (strncmp(query, "Xfer:features:read:target.xml:", 576 } else if (strncmp(query, "Xfer:features:read:target.xml:",
539 strlen("Xfer:features:read:target.xml:")) == 0) { 577 strlen("Xfer:features:read:target.xml:")) == 0) {
540 SendReply(target_xml); 578 SendReply(target_xml);
@@ -543,9 +581,9 @@ static void HandleQuery() {
543 SendReply(buffer.c_str()); 581 SendReply(buffer.c_str());
544 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { 582 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
545 std::string val = "m"; 583 std::string val = "m";
546 for (int core = 0; core < Core::NUM_CPU_CORES; core++) { 584 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
547 auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 585 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
548 for (auto thread : threads) { 586 for (const auto& thread : threads) {
549 val += fmt::format("{:x}", thread->GetThreadId()); 587 val += fmt::format("{:x}", thread->GetThreadId());
550 val += ","; 588 val += ",";
551 } 589 }
@@ -554,6 +592,31 @@ static void HandleQuery() {
554 SendReply(val.c_str()); 592 SendReply(val.c_str());
555 } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) { 593 } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
556 SendReply("l"); 594 SendReply("l");
595 } else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) {
596 std::string buffer;
597 buffer += "l<?xml version=\"1.0\"?>";
598 buffer += "<threads>";
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
601 for (const auto& thread : threads) {
602 buffer +=
603 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
604 thread->GetThreadId(), core, thread->GetThreadId());
605 }
606 }
607 buffer += "</threads>";
608 SendReply(buffer.c_str());
609 } else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) {
610 std::string buffer;
611 buffer += "l<?xml version=\"1.0\"?>";
612 buffer += "<library-list>";
613 for (const auto& module : modules) {
614 buffer +=
615 fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*",
616 module.name, module.beg);
617 }
618 buffer += "</library-list>";
619 SendReply(buffer.c_str());
557 } else { 620 } else {
558 SendReply(""); 621 SendReply("");
559 } 622 }
@@ -561,33 +624,27 @@ static void HandleQuery() {
561 624
562/// Handle set thread command from gdb client. 625/// Handle set thread command from gdb client.
563static void HandleSetThread() { 626static void HandleSetThread() {
564 if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) { 627 int thread_id = -1;
565 int thread_id = -1; 628 if (command_buffer[2] != '-') {
566 if (command_buffer[2] != '-') { 629 thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 2));
567 thread_id = static_cast<int>(HexToInt( 630 }
568 command_buffer + 2, 631 if (thread_id >= 1) {
569 command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/)); 632 current_thread = FindThreadById(thread_id);
570 } 633 }
571 if (thread_id >= 1) { 634 if (!current_thread) {
572 current_thread = FindThreadById(thread_id); 635 thread_id = 1;
573 } 636 current_thread = FindThreadById(thread_id);
574 if (!current_thread) { 637 }
575 thread_id = 1; 638 if (current_thread) {
576 current_thread = FindThreadById(thread_id); 639 SendReply("OK");
577 } 640 return;
578 if (current_thread) {
579 SendReply("OK");
580 return;
581 }
582 } 641 }
583 SendReply("E01"); 642 SendReply("E01");
584} 643}
585 644
586/// Handle thread alive command from gdb client. 645/// Handle thread alive command from gdb client.
587static void HandleThreadAlive() { 646static void HandleThreadAlive() {
588 int thread_id = static_cast<int>( 647 int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1));
589 HexToInt(command_buffer + 1,
590 command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
591 if (thread_id == 0) { 648 if (thread_id == 0) {
592 thread_id = 1; 649 thread_id = 1;
593 } 650 }
@@ -610,16 +667,23 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
610 667
611 latest_signal = signal; 668 latest_signal = signal;
612 669
670 if (!thread) {
671 full = false;
672 }
673
613 std::string buffer; 674 std::string buffer;
614 if (full) { 675 if (full) {
615 buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER, 676 buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal,
616 Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, 677 PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
617 Common::swap64(RegRead(SP_REGISTER, thread))); 678 Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER,
679 Common::swap64(RegRead(LR_REGISTER, thread)));
618 } else { 680 } else {
619 buffer = fmt::format("T{:02x};", latest_signal); 681 buffer = fmt::format("T{:02x}", latest_signal);
620 } 682 }
621 683
622 buffer += fmt::format("thread:{:x};", thread->GetThreadId()); 684 if (thread) {
685 buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
686 }
623 687
624 SendReply(buffer.c_str()); 688 SendReply(buffer.c_str());
625} 689}
@@ -711,8 +775,12 @@ static void ReadRegister() {
711 LongToGdbHex(reply, RegRead(id, current_thread)); 775 LongToGdbHex(reply, RegRead(id, current_thread));
712 } else if (id == CPSR_REGISTER) { 776 } else if (id == CPSR_REGISTER) {
713 IntToGdbHex(reply, (u32)RegRead(id, current_thread)); 777 IntToGdbHex(reply, (u32)RegRead(id, current_thread));
778 } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
779 LongToGdbHex(reply, RegRead(id, current_thread));
780 } else if (id == FPSCR_REGISTER) {
781 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
714 } else { 782 } else {
715 return SendReply("E01"); 783 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
716 } 784 }
717 785
718 SendReply(reinterpret_cast<char*>(reply)); 786 SendReply(reinterpret_cast<char*>(reply));
@@ -729,7 +797,7 @@ static void ReadRegisters() {
729 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); 797 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
730 } 798 }
731 799
732 bufptr += (32 * 16); 800 bufptr += 32 * 16;
733 801
734 LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread)); 802 LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
735 803
@@ -739,6 +807,16 @@ static void ReadRegisters() {
739 807
740 bufptr += 8; 808 bufptr += 8;
741 809
810 for (int reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) {
811 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
812 }
813
814 bufptr += 32 * 32;
815
816 LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
817
818 bufptr += 8;
819
742 SendReply(reinterpret_cast<char*>(buffer)); 820 SendReply(reinterpret_cast<char*>(buffer));
743} 821}
744 822
@@ -759,10 +837,17 @@ static void WriteRegister() {
759 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); 837 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
760 } else if (id == CPSR_REGISTER) { 838 } else if (id == CPSR_REGISTER) {
761 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); 839 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
840 } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
841 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
842 } else if (id == FPSCR_REGISTER) {
843 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
762 } else { 844 } else {
763 return SendReply("E01"); 845 RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
764 } 846 }
765 847
848 // Update Unicorn context skipping scheduler, no running threads at this point
849 Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
850
766 SendReply("OK"); 851 SendReply("OK");
767} 852}
768 853
@@ -773,18 +858,25 @@ static void WriteRegisters() {
773 if (command_buffer[0] != 'G') 858 if (command_buffer[0] != 'G')
774 return SendReply("E01"); 859 return SendReply("E01");
775 860
776 for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { 861 for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
777 if (reg <= SP_REGISTER) { 862 if (reg <= SP_REGISTER) {
778 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); 863 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
779 } else if (reg == PC_REGISTER) { 864 } else if (reg == PC_REGISTER) {
780 RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); 865 RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
781 } else if (reg == CPSR_REGISTER) { 866 } else if (reg == CPSR_REGISTER) {
782 RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); 867 RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
868 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) {
869 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
870 } else if (reg == FPSCR_REGISTER) {
871 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
783 } else { 872 } else {
784 UNIMPLEMENTED(); 873 UNIMPLEMENTED();
785 } 874 }
786 } 875 }
787 876
877 // Update Unicorn context skipping scheduler, no running threads at this point
878 Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
879
788 SendReply("OK"); 880 SendReply("OK");
789} 881}
790 882
@@ -806,6 +898,10 @@ static void ReadMemory() {
806 SendReply("E01"); 898 SendReply("E01");
807 } 899 }
808 900
901 if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
902 return SendReply("E00");
903 }
904
809 if (!Memory::IsValidVirtualAddress(addr)) { 905 if (!Memory::IsValidVirtualAddress(addr)) {
810 return SendReply("E00"); 906 return SendReply("E00");
811 } 907 }
@@ -840,16 +936,18 @@ static void WriteMemory() {
840} 936}
841 937
842void Break(bool is_memory_break) { 938void Break(bool is_memory_break) {
843 if (!halt_loop) { 939 send_trap = true;
844 halt_loop = true;
845 send_trap = true;
846 }
847 940
848 memory_break = is_memory_break; 941 memory_break = is_memory_break;
849} 942}
850 943
851/// Tell the CPU that it should perform a single step. 944/// Tell the CPU that it should perform a single step.
852static void Step() { 945static void Step() {
946 if (command_length > 1) {
947 RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
948 // Update Unicorn context skipping scheduler, no running threads at this point
949 Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
950 }
853 step_loop = true; 951 step_loop = true;
854 halt_loop = true; 952 halt_loop = true;
855 send_trap = true; 953 send_trap = true;
@@ -1090,6 +1188,8 @@ static void Init(u16 port) {
1090 breakpoints_read.clear(); 1188 breakpoints_read.clear();
1091 breakpoints_write.clear(); 1189 breakpoints_write.clear();
1092 1190
1191 modules.clear();
1192
1093 // Start gdb server 1193 // Start gdb server
1094 LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port); 1194 LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port);
1095 1195
@@ -1192,8 +1292,12 @@ void SetCpuStepFlag(bool is_step) {
1192 1292
1193void SendTrap(Kernel::Thread* thread, int trap) { 1293void SendTrap(Kernel::Thread* thread, int trap) {
1194 if (send_trap) { 1294 if (send_trap) {
1295 if (!halt_loop || current_thread == thread) {
1296 current_thread = thread;
1297 SendSignal(thread, trap);
1298 }
1299 halt_loop = true;
1195 send_trap = false; 1300 send_trap = false;
1196 SendSignal(thread, trap);
1197 } 1301 }
1198} 1302}
1199}; // namespace GDBStub 1303}; // namespace GDBStub
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
index f2418c9e4..a6b50c26c 100644
--- a/src/core/gdbstub/gdbstub.h
+++ b/src/core/gdbstub/gdbstub.h
@@ -6,6 +6,7 @@
6 6
7#pragma once 7#pragma once
8 8
9#include <string>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
11 12
@@ -51,6 +52,9 @@ bool IsServerEnabled();
51/// Returns true if there is an active socket connection. 52/// Returns true if there is an active socket connection.
52bool IsConnected(); 53bool IsConnected();
53 54
55/// Register module.
56void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
57
54/** 58/**
55 * Signal to the gdbstub server that it should halt CPU execution. 59 * Signal to the gdbstub server that it should halt CPU execution.
56 * 60 *
@@ -80,10 +84,10 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
80 */ 84 */
81bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); 85bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
82 86
83// If set to true, the CPU will halt at the beginning of the next CPU loop. 87/// If set to true, the CPU will halt at the beginning of the next CPU loop.
84bool GetCpuHaltFlag(); 88bool GetCpuHaltFlag();
85 89
86// If set to true and the CPU is halted, the CPU will step one instruction. 90/// If set to true and the CPU is halted, the CPU will step one instruction.
87bool GetCpuStepFlag(); 91bool GetCpuStepFlag();
88 92
89/** 93/**
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 609cdbff2..2532dd450 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -214,8 +214,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
214 (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32); 214 (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
215 ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented"); 215 ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
216 216
217 ASSERT_MSG(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); 217 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
218 ASSERT_MSG(move_objects.size() == handle_descriptor_header->num_handles_to_move); 218 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
219 219
220 // We don't make a distinction between copy and move handles when translating since HLE 220 // We don't make a distinction between copy and move handles when translating since HLE
221 // services don't deal with handles directly. However, the guest applications might check 221 // services don't deal with handles directly. However, the guest applications might check
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 1b4b649d8..8bf273b22 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -27,12 +27,12 @@ public:
27 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 27 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
28 {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, 28 {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
29 {2, &IAudioOut::StopAudioOut, "StopAudioOut"}, 29 {2, &IAudioOut::StopAudioOut, "StopAudioOut"},
30 {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"}, 30 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
31 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, 31 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
32 {5, &IAudioOut::GetReleasedAudioOutBuffer, "GetReleasedAudioOutBuffer"}, 32 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
33 {6, nullptr, "ContainsAudioOutBuffer"}, 33 {6, nullptr, "ContainsAudioOutBuffer"},
34 {7, nullptr, "AppendAudioOutBufferAuto"}, 34 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
35 {8, nullptr, "GetReleasedAudioOutBufferAuto"}, 35 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
36 {9, nullptr, "GetAudioOutBufferCount"}, 36 {9, nullptr, "GetAudioOutBufferCount"},
37 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 37 {10, nullptr, "GetAudioOutPlayedSampleCount"},
38 {11, nullptr, "FlushAudioOutBuffers"}, 38 {11, nullptr, "FlushAudioOutBuffers"},
@@ -96,7 +96,7 @@ private:
96 rb.PushCopyObjects(buffer_event); 96 rb.PushCopyObjects(buffer_event);
97 } 97 }
98 98
99 void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { 99 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
100 LOG_WARNING(Service_Audio, "(STUBBED) called"); 100 LOG_WARNING(Service_Audio, "(STUBBED) called");
101 IPC::RequestParser rp{ctx}; 101 IPC::RequestParser rp{ctx};
102 102
@@ -107,7 +107,7 @@ private:
107 rb.Push(RESULT_SUCCESS); 107 rb.Push(RESULT_SUCCESS);
108 } 108 }
109 109
110 void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) { 110 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
111 LOG_WARNING(Service_Audio, "(STUBBED) called"); 111 LOG_WARNING(Service_Audio, "(STUBBED) called");
112 112
113 // TODO(st4rk): This is how libtransistor currently implements the 113 // TODO(st4rk): This is how libtransistor currently implements the
@@ -163,7 +163,7 @@ private:
163 AudioState audio_out_state; 163 AudioState audio_out_state;
164}; 164};
165 165
166void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { 166void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
167 LOG_WARNING(Service_Audio, "(STUBBED) called"); 167 LOG_WARNING(Service_Audio, "(STUBBED) called");
168 IPC::RequestParser rp{ctx}; 168 IPC::RequestParser rp{ctx};
169 169
@@ -179,7 +179,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
179 rb.Push<u32>(1); 179 rb.Push<u32>(1);
180} 180}
181 181
182void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { 182void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
183 LOG_WARNING(Service_Audio, "(STUBBED) called"); 183 LOG_WARNING(Service_Audio, "(STUBBED) called");
184 184
185 if (!audio_out_interface) { 185 if (!audio_out_interface) {
@@ -196,10 +196,10 @@ void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
196} 196}
197 197
198AudOutU::AudOutU() : ServiceFramework("audout:u") { 198AudOutU::AudOutU() : ServiceFramework("audout:u") {
199 static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, 199 static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
200 {1, &AudOutU::OpenAudioOut, "OpenAudioOut"}, 200 {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
201 {2, nullptr, "ListAudioOutsAuto"}, 201 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
202 {3, nullptr, "OpenAudioOutAuto"}}; 202 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
203 RegisterHandlers(functions); 203 RegisterHandlers(functions);
204} 204}
205 205
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index 1f9bb9bcf..847d86aa6 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -22,8 +22,8 @@ public:
22private: 22private:
23 std::shared_ptr<IAudioOut> audio_out_interface; 23 std::shared_ptr<IAudioOut> audio_out_interface;
24 24
25 void ListAudioOuts(Kernel::HLERequestContext& ctx); 25 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
26 void OpenAudioOut(Kernel::HLERequestContext& ctx); 26 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
27 27
28 enum class PcmFormat : u32 { 28 enum class PcmFormat : u32 {
29 Invalid = 0, 29 Invalid = 0,
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 2da936b27..b7f591c6d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -47,7 +47,7 @@ public:
47 47
48 // Start the audio event 48 // Start the audio event
49 CoreTiming::ScheduleEvent(audio_ticks, audio_event); 49 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
50 voice_status_list.reserve(worker_params.voice_count); 50 voice_status_list.resize(worker_params.voice_count);
51 } 51 }
52 ~IAudioRenderer() { 52 ~IAudioRenderer() {
53 CoreTiming::UnscheduleEvent(audio_event, 0); 53 CoreTiming::UnscheduleEvent(audio_event, 0);
@@ -87,8 +87,6 @@ private:
87 memory_pool[i].state = MemoryPoolStates::Attached; 87 memory_pool[i].state = MemoryPoolStates::Attached;
88 else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach) 88 else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
89 memory_pool[i].state = MemoryPoolStates::Detached; 89 memory_pool[i].state = MemoryPoolStates::Detached;
90 else
91 memory_pool[i].state = mem_pool_info[i].pool_state;
92 } 90 }
93 std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(), 91 std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
94 response_data.memory_pools_size); 92 response_data.memory_pools_size);
@@ -183,7 +181,9 @@ private:
183 behavior_size = 0xb0; 181 behavior_size = 0xb0;
184 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; 182 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
185 voices_size = config.voice_count * 0x10; 183 voices_size = config.voice_count * 0x10;
184 voice_resource_size = 0x0;
186 effects_size = config.effect_count * 0x10; 185 effects_size = config.effect_count * 0x10;
186 mixes_size = 0x0;
187 sinks_size = config.sink_count * 0x20; 187 sinks_size = config.sink_count * 0x20;
188 performance_manager_size = 0x10; 188 performance_manager_size = 0x10;
189 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + 189 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 54a151c26..0d951084b 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -148,6 +148,24 @@ private:
148 148
149 LOG_DEBUG(Service_NIFM, "called"); 149 LOG_DEBUG(Service_NIFM, "called");
150 } 150 }
151 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
152 LOG_WARNING(Service_NIFM, "(STUBBED) called");
153 IPC::ResponseBuilder rb{ctx, 3};
154 rb.Push(RESULT_SUCCESS);
155 rb.Push<u8>(0);
156 }
157 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
158 LOG_WARNING(Service_NIFM, "(STUBBED) called");
159 IPC::ResponseBuilder rb{ctx, 3};
160 rb.Push(RESULT_SUCCESS);
161 rb.Push<u8>(0);
162 }
163 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
164 LOG_WARNING(Service_NIFM, "(STUBBED) called");
165 IPC::ResponseBuilder rb{ctx, 3};
166 rb.Push(RESULT_SUCCESS);
167 rb.Push<u8>(0);
168 }
151}; 169};
152 170
153IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { 171IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
@@ -167,11 +185,11 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
167 {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, 185 {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
168 {15, nullptr, "GetCurrentIpConfigInfo"}, 186 {15, nullptr, "GetCurrentIpConfigInfo"},
169 {16, nullptr, "SetWirelessCommunicationEnabled"}, 187 {16, nullptr, "SetWirelessCommunicationEnabled"},
170 {17, nullptr, "IsWirelessCommunicationEnabled"}, 188 {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
171 {18, nullptr, "GetInternetConnectionStatus"}, 189 {18, nullptr, "GetInternetConnectionStatus"},
172 {19, nullptr, "SetEthernetCommunicationEnabled"}, 190 {19, nullptr, "SetEthernetCommunicationEnabled"},
173 {20, nullptr, "IsEthernetCommunicationEnabled"}, 191 {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
174 {21, nullptr, "IsAnyInternetRequestAccepted"}, 192 {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
175 {22, nullptr, "IsAnyForegroundRequestAccepted"}, 193 {22, nullptr, "IsAnyForegroundRequestAccepted"},
176 {23, nullptr, "PutToSleep"}, 194 {23, nullptr, "PutToSleep"},
177 {24, nullptr, "WakeUp"}, 195 {24, nullptr, "WakeUp"},
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 32648bdd9..6aa1e2511 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -19,10 +19,9 @@ void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
19void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { 19void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
20 LOG_WARNING(Service, "(STUBBED) called"); 20 LOG_WARNING(Service, "(STUBBED) called");
21 21
22 IPC::ResponseBuilder rb{ctx, 3}; 22 IPC::ResponseBuilder rb{ctx, 2};
23 23
24 rb.Push(RESULT_SUCCESS); 24 rb.Push(RESULT_SUCCESS);
25 rb.Push<u32>(0); // bsd errno
26} 25}
27 26
28void BSD::Socket(Kernel::HLERequestContext& ctx) { 27void BSD::Socket(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index eb7feb617..5fdb1d289 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/file_sys/romfs_factory.h" 11#include "core/file_sys/romfs_factory.h"
12#include "core/gdbstub/gdbstub.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/resource_limit.h" 14#include "core/hle/kernel/resource_limit.h"
14#include "core/hle/service/filesystem/filesystem.h" 15#include "core/hle/service/filesystem/filesystem.h"
@@ -133,6 +134,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
133 next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); 134 next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
134 if (next_load_addr) { 135 if (next_load_addr) {
135 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 136 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
137 // Register module with GDBStub
138 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
136 } else { 139 } else {
137 next_load_addr = load_addr; 140 next_load_addr = load_addr;
138 } 141 }
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index da064f8e3..0fd930ae2 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -7,10 +7,12 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/string_util.h"
10#include "common/swap.h" 11#include "common/swap.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/file_sys/program_metadata.h" 13#include "core/file_sys/program_metadata.h"
13#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
15#include "core/gdbstub/gdbstub.h"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h" 17#include "core/hle/kernel/resource_limit.h"
16#include "core/hle/service/filesystem/filesystem.h" 18#include "core/hle/service/filesystem/filesystem.h"
@@ -259,6 +261,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
259 next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr); 261 next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);
260 if (next_load_addr) { 262 if (next_load_addr) {
261 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 263 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
264 // Register module with GDBStub
265 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
262 } else { 266 } else {
263 next_load_addr = load_addr; 267 next_load_addr = load_addr;
264 } 268 }
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 3853cfa1a..4d7c69a22 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/gdbstub/gdbstub.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/resource_limit.h" 14#include "core/hle/kernel/resource_limit.h"
14#include "core/loader/nro.h" 15#include "core/loader/nro.h"
@@ -115,6 +116,9 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
115 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 116 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
116 Core::CurrentProcess()->LoadModule(codeset, load_base); 117 Core::CurrentProcess()->LoadModule(codeset, load_base);
117 118
119 // Register module with GDBStub
120 GDBStub::RegisterModule(codeset->name, load_base, load_base);
121
118 return true; 122 return true;
119} 123}
120 124
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 7f84e4b1b..1c629e21f 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -10,6 +10,7 @@
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/gdbstub/gdbstub.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/resource_limit.h" 15#include "core/hle/kernel/resource_limit.h"
15#include "core/loader/nso.h" 16#include "core/loader/nso.h"
@@ -147,6 +148,9 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
147 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 148 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
148 Core::CurrentProcess()->LoadModule(codeset, load_base); 149 Core::CurrentProcess()->LoadModule(codeset, load_base);
149 150
151 // Register module with GDBStub
152 GDBStub::RegisterModule(codeset->name, load_base, load_base);
153
150 return load_base + image_size; 154 return load_base + image_size;
151} 155}
152 156
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 3bca16364..dfbf80abd 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -398,27 +398,6 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
398 return regs.reg_array[method]; 398 return regs.reg_array[method];
399} 399}
400 400
401bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const {
402 // The Vertex stage is always enabled.
403 if (stage == Regs::ShaderStage::Vertex)
404 return true;
405
406 switch (stage) {
407 case Regs::ShaderStage::TesselationControl:
408 return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::TesselationControl)]
409 .enable != 0;
410 case Regs::ShaderStage::TesselationEval:
411 return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::TesselationEval)]
412 .enable != 0;
413 case Regs::ShaderStage::Geometry:
414 return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::Geometry)].enable != 0;
415 case Regs::ShaderStage::Fragment:
416 return regs.shader_config[static_cast<size_t>(Regs::ShaderProgram::Fragment)].enable != 0;
417 }
418
419 UNREACHABLE();
420}
421
422void Maxwell3D::ProcessClearBuffers() { 401void Maxwell3D::ProcessClearBuffers() {
423 ASSERT(regs.clear_buffers.R == regs.clear_buffers.G && 402 ASSERT(regs.clear_buffers.R == regs.clear_buffers.G &&
424 regs.clear_buffers.R == regs.clear_buffers.B && 403 regs.clear_buffers.R == regs.clear_buffers.B &&
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 5a7cf0107..6f0170ff7 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -379,6 +379,14 @@ public:
379 } 379 }
380 }; 380 };
381 381
382 bool IsShaderConfigEnabled(size_t index) const {
383 // The VertexB is always enabled.
384 if (index == static_cast<size_t>(Regs::ShaderProgram::VertexB)) {
385 return true;
386 }
387 return shader_config[index].enable != 0;
388 }
389
382 union { 390 union {
383 struct { 391 struct {
384 INSERT_PADDING_WORDS(0x45); 392 INSERT_PADDING_WORDS(0x45);
@@ -780,9 +788,6 @@ public:
780 /// Returns the texture information for a specific texture in a specific shader stage. 788 /// Returns the texture information for a specific texture in a specific shader stage.
781 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; 789 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
782 790
783 /// Returns whether the specified shader stage is enabled or not.
784 bool IsShaderStageEnabled(Regs::ShaderStage stage) const;
785
786private: 791private:
787 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 792 std::unordered_map<u32, std::vector<u32>> uploaded_macros;
788 793
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 2bc1782ad..65fa1495f 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -142,6 +142,7 @@ enum class PredCondition : u64 {
142 GreaterThan = 4, 142 GreaterThan = 4,
143 NotEqual = 5, 143 NotEqual = 5,
144 GreaterEqual = 6, 144 GreaterEqual = 6,
145 LessThanWithNan = 9,
145 NotEqualWithNan = 13, 146 NotEqualWithNan = 13,
146 // TODO(Subv): Other condition types 147 // TODO(Subv): Other condition types
147}; 148};
@@ -201,6 +202,11 @@ enum class IMinMaxExchange : u64 {
201 XHi = 3, 202 XHi = 3,
202}; 203};
203 204
205enum class FlowCondition : u64 {
206 Always = 0xF,
207 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
208};
209
204union Instruction { 210union Instruction {
205 Instruction& operator=(const Instruction& instr) { 211 Instruction& operator=(const Instruction& instr) {
206 value = instr.value; 212 value = instr.value;
@@ -298,6 +304,13 @@ union Instruction {
298 } iadd32i; 304 } iadd32i;
299 305
300 union { 306 union {
307 BitField<53, 1, u64> negate_b;
308 BitField<54, 1, u64> abs_a;
309 BitField<56, 1, u64> negate_a;
310 BitField<57, 1, u64> abs_b;
311 } fadd32i;
312
313 union {
301 BitField<20, 8, u64> shift_position; 314 BitField<20, 8, u64> shift_position;
302 BitField<28, 8, u64> shift_length; 315 BitField<28, 8, u64> shift_length;
303 BitField<48, 1, u64> negate_b; 316 BitField<48, 1, u64> negate_b;
@@ -309,6 +322,10 @@ union Instruction {
309 } bfe; 322 } bfe;
310 323
311 union { 324 union {
325 BitField<0, 5, FlowCondition> cond;
326 } flow;
327
328 union {
312 BitField<48, 1, u64> negate_b; 329 BitField<48, 1, u64> negate_b;
313 BitField<49, 1, u64> negate_c; 330 BitField<49, 1, u64> negate_c;
314 } ffma; 331 } ffma;
@@ -487,6 +504,7 @@ public:
487 FADD_C, 504 FADD_C,
488 FADD_R, 505 FADD_R,
489 FADD_IMM, 506 FADD_IMM,
507 FADD32I,
490 FMUL_C, 508 FMUL_C,
491 FMUL_R, 509 FMUL_R,
492 FMUL_IMM, 510 FMUL_IMM,
@@ -679,13 +697,14 @@ private:
679 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), 697 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
680 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), 698 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
681 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 699 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
682 INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 700 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
683 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 701 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
684 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 702 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
685 INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), 703 INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"),
686 INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"), 704 INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"),
687 INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"), 705 INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"),
688 INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"), 706 INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"),
707 INST("000010----------", Id::FADD32I, Type::ArithmeticImmediate, "FADD32I"),
689 INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), 708 INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
690 INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), 709 INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
691 INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), 710 INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index ea138d402..eecbc5ff0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -15,6 +15,7 @@
15#include "common/microprofile.h" 15#include "common/microprofile.h"
16#include "common/scope_exit.h" 16#include "common/scope_exit.h"
17#include "core/core.h" 17#include "core/core.h"
18#include "core/frontend/emu_window.h"
18#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
19#include "core/settings.h" 20#include "core/settings.h"
20#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
@@ -22,6 +23,7 @@
22#include "video_core/renderer_opengl/gl_shader_gen.h" 23#include "video_core/renderer_opengl/gl_shader_gen.h"
23#include "video_core/renderer_opengl/maxwell_to_gl.h" 24#include "video_core/renderer_opengl/maxwell_to_gl.h"
24#include "video_core/renderer_opengl/renderer_opengl.h" 25#include "video_core/renderer_opengl/renderer_opengl.h"
26#include "video_core/video_core.h"
25 27
26using Maxwell = Tegra::Engines::Maxwell3D::Regs; 28using Maxwell = Tegra::Engines::Maxwell3D::Regs;
27using PixelFormat = SurfaceParams::PixelFormat; 29using PixelFormat = SurfaceParams::PixelFormat;
@@ -181,6 +183,19 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
181 return {array_ptr, buffer_offset}; 183 return {array_ptr, buffer_offset};
182} 184}
183 185
186static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program) {
187 auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
188
189 // Fetch program code from memory
190 GLShader::ProgramCode program_code;
191 auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)];
192 const u64 gpu_address{gpu.regs.code_address.CodeAddress() + shader_config.offset};
193 const boost::optional<VAddr> cpu_address{gpu.memory_manager.GpuToCpuAddress(gpu_address)};
194 Memory::ReadBlock(*cpu_address, program_code.data(), program_code.size() * sizeof(u64));
195
196 return program_code;
197}
198
184void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { 199void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
185 // Helper function for uploading uniform data 200 // Helper function for uploading uniform data
186 const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { 201 const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) {
@@ -193,26 +208,23 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
193 }; 208 };
194 209
195 auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); 210 auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
196 ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!");
197 211
198 // Next available bindpoints to use when uploading the const buffers and textures to the GLSL 212 // Next available bindpoints to use when uploading the const buffers and textures to the GLSL
199 // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. 213 // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
200 u32 current_constbuffer_bindpoint = uniform_buffers.size(); 214 u32 current_constbuffer_bindpoint = uniform_buffers.size();
201 u32 current_texture_bindpoint = 0; 215 u32 current_texture_bindpoint = 0;
202 216
203 for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { 217 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
204 auto& shader_config = gpu.regs.shader_config[index]; 218 auto& shader_config = gpu.regs.shader_config[index];
205 const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; 219 const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)};
206 220
207 const auto& stage = index - 1; // Stage indices are 0 - 5
208
209 const bool is_enabled = gpu.IsShaderStageEnabled(static_cast<Maxwell::ShaderStage>(stage));
210
211 // Skip stages that are not enabled 221 // Skip stages that are not enabled
212 if (!is_enabled) { 222 if (!gpu.regs.IsShaderConfigEnabled(index)) {
213 continue; 223 continue;
214 } 224 }
215 225
226 const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
227
216 GLShader::MaxwellUniformData ubo{}; 228 GLShader::MaxwellUniformData ubo{};
217 ubo.SetFromRegs(gpu.state.shader_stages[stage]); 229 ubo.SetFromRegs(gpu.state.shader_stages[stage]);
218 std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); 230 std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
@@ -228,16 +240,21 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
228 buffer_ptr += sizeof(GLShader::MaxwellUniformData); 240 buffer_ptr += sizeof(GLShader::MaxwellUniformData);
229 buffer_offset += sizeof(GLShader::MaxwellUniformData); 241 buffer_offset += sizeof(GLShader::MaxwellUniformData);
230 242
231 // Fetch program code from memory 243 GLShader::ShaderSetup setup{GetShaderProgramCode(program)};
232 GLShader::ProgramCode program_code;
233 const u64 gpu_address{gpu.regs.code_address.CodeAddress() + shader_config.offset};
234 const boost::optional<VAddr> cpu_address{gpu.memory_manager.GpuToCpuAddress(gpu_address)};
235 Memory::ReadBlock(*cpu_address, program_code.data(), program_code.size() * sizeof(u64));
236 GLShader::ShaderSetup setup{std::move(program_code)};
237
238 GLShader::ShaderEntries shader_resources; 244 GLShader::ShaderEntries shader_resources;
239 245
240 switch (program) { 246 switch (program) {
247 case Maxwell::ShaderProgram::VertexA: {
248 // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders.
249 // Conventional HW does not support this, so we combine VertexA and VertexB into one
250 // stage here.
251 setup.SetProgramB(GetShaderProgramCode(Maxwell::ShaderProgram::VertexB));
252 GLShader::MaxwellVSConfig vs_config{setup};
253 shader_resources =
254 shader_program_manager->UseProgrammableVertexShader(vs_config, setup);
255 break;
256 }
257
241 case Maxwell::ShaderProgram::VertexB: { 258 case Maxwell::ShaderProgram::VertexB: {
242 GLShader::MaxwellVSConfig vs_config{setup}; 259 GLShader::MaxwellVSConfig vs_config{setup};
243 shader_resources = 260 shader_resources =
@@ -268,6 +285,12 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
268 current_texture_bindpoint = 285 current_texture_bindpoint =
269 SetupTextures(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, 286 SetupTextures(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program,
270 current_texture_bindpoint, shader_resources.texture_samplers); 287 current_texture_bindpoint, shader_resources.texture_samplers);
288
289 // When VertexA is enabled, we have dual vertex shaders
290 if (program == Maxwell::ShaderProgram::VertexA) {
291 // VertexB was combined with VertexA, so we skip the VertexB iteration
292 index++;
293 }
271 } 294 }
272 295
273 shader_program_manager->UseTrivialGeometryShader(); 296 shader_program_manager->UseTrivialGeometryShader();
@@ -301,9 +324,6 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
301 bool using_depth_fb) { 324 bool using_depth_fb) {
302 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 325 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
303 326
304 // Sync the depth test state before configuring the framebuffer surfaces.
305 SyncDepthTestState();
306
307 // TODO(bunnei): Implement this 327 // TODO(bunnei): Implement this
308 const bool has_stencil = false; 328 const bool has_stencil = false;
309 329
@@ -368,11 +388,20 @@ void RasterizerOpenGL::Clear() {
368 if (regs.clear_buffers.Z) { 388 if (regs.clear_buffers.Z) {
369 clear_mask |= GL_DEPTH_BUFFER_BIT; 389 clear_mask |= GL_DEPTH_BUFFER_BIT;
370 use_depth_fb = true; 390 use_depth_fb = true;
391
392 // Always enable the depth write when clearing the depth buffer. The depth write mask is
393 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
394 state.depth.test_enabled = true;
395 state.depth.write_mask = GL_TRUE;
396 state.depth.test_func = GL_ALWAYS;
397 state.Apply();
371 } 398 }
372 399
373 if (clear_mask == 0) 400 if (clear_mask == 0)
374 return; 401 return;
375 402
403 ScopeAcquireGLContext acquire_context;
404
376 auto [dirty_color_surface, dirty_depth_surface] = 405 auto [dirty_color_surface, dirty_depth_surface] =
377 ConfigureFramebuffers(use_color_fb, use_depth_fb); 406 ConfigureFramebuffers(use_color_fb, use_depth_fb);
378 407
@@ -399,9 +428,12 @@ void RasterizerOpenGL::DrawArrays() {
399 MICROPROFILE_SCOPE(OpenGL_Drawing); 428 MICROPROFILE_SCOPE(OpenGL_Drawing);
400 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 429 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
401 430
431 ScopeAcquireGLContext acquire_context;
432
402 auto [dirty_color_surface, dirty_depth_surface] = 433 auto [dirty_color_surface, dirty_depth_surface] =
403 ConfigureFramebuffers(true, regs.zeta.Address() != 0); 434 ConfigureFramebuffers(true, regs.zeta.Address() != 0);
404 435
436 SyncDepthTestState();
405 SyncBlendState(); 437 SyncBlendState();
406 SyncCullMode(); 438 SyncCullMode();
407 439
@@ -605,9 +637,6 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
605 auto& gpu = Core::System::GetInstance().GPU(); 637 auto& gpu = Core::System::GetInstance().GPU();
606 auto& maxwell3d = gpu.Get3DEngine(); 638 auto& maxwell3d = gpu.Get3DEngine();
607 639
608 ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage),
609 "Attempted to upload constbuffer of disabled shader stage");
610
611 // Reset all buffer draw state for this stage. 640 // Reset all buffer draw state for this stage.
612 for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) { 641 for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) {
613 buffer.bindpoint = 0; 642 buffer.bindpoint = 0;
@@ -674,9 +703,6 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program,
674 auto& gpu = Core::System::GetInstance().GPU(); 703 auto& gpu = Core::System::GetInstance().GPU();
675 auto& maxwell3d = gpu.Get3DEngine(); 704 auto& maxwell3d = gpu.Get3DEngine();
676 705
677 ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage),
678 "Attempted to upload textures of disabled shader stage");
679
680 ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units), 706 ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
681 "Exceeded the number of active textures."); 707 "Exceeded the number of active textures.");
682 708
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 323ff7408..c171c4c5b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -105,6 +105,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
105 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 105 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
106 true}, // BC7U 106 true}, // BC7U
107 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 107 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
108 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8
108 109
109 // DepthStencil formats 110 // DepthStencil formats
110 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, 111 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
@@ -112,6 +113,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
112 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, 113 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
113 false}, // S8Z24 114 false}, // S8Z24
114 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 115 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
116 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, ComponentType::UNorm,
117 false}, // Z16
115}}; 118}};
116 119
117static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { 120static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
@@ -194,8 +197,9 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
194 MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, 197 MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
195 MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, 198 MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
196 MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>, 199 MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
197 MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, 200 MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::Z24S8>,
198 MortonCopy<true, PixelFormat::Z32F>, 201 MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
202 MortonCopy<true, PixelFormat::Z16>,
199}; 203};
200 204
201static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), 205static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
@@ -215,10 +219,12 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
215 nullptr, 219 nullptr,
216 nullptr, 220 nullptr,
217 nullptr, 221 nullptr,
218 MortonCopy<false, PixelFormat::ABGR8>, 222 nullptr,
223 MortonCopy<false, PixelFormat::G8R8>,
219 MortonCopy<false, PixelFormat::Z24S8>, 224 MortonCopy<false, PixelFormat::Z24S8>,
220 MortonCopy<false, PixelFormat::S8Z24>, 225 MortonCopy<false, PixelFormat::S8Z24>,
221 MortonCopy<false, PixelFormat::Z32F>, 226 MortonCopy<false, PixelFormat::Z32F>,
227 MortonCopy<false, PixelFormat::Z16>,
222}; 228};
223 229
224// Allocate an uninitialized texture of appropriate size and format for the surface 230// Allocate an uninitialized texture of appropriate size and format for the surface
@@ -271,9 +277,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
271 277
272 S8Z24 input_pixel{}; 278 S8Z24 input_pixel{};
273 Z24S8 output_pixel{}; 279 Z24S8 output_pixel{};
280 const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
274 for (size_t y = 0; y < height; ++y) { 281 for (size_t y = 0; y < height; ++y) {
275 for (size_t x = 0; x < width; ++x) { 282 for (size_t x = 0; x < width; ++x) {
276 const size_t offset{y * width + x}; 283 const size_t offset{bpp * (y * width + x)};
277 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 284 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
278 output_pixel.s8.Assign(input_pixel.s8); 285 output_pixel.s8.Assign(input_pixel.s8);
279 output_pixel.z24.Assign(input_pixel.z24); 286 output_pixel.z24.Assign(input_pixel.z24);
@@ -281,6 +288,19 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
281 } 288 }
282 } 289 }
283} 290}
291
292static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
293 const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8)};
294 for (size_t y = 0; y < height; ++y) {
295 for (size_t x = 0; x < width; ++x) {
296 const size_t offset{bpp * (y * width + x)};
297 const u8 temp{data[offset]};
298 data[offset] = data[offset + 1];
299 data[offset + 1] = temp;
300 }
301 }
302}
303
284/** 304/**
285 * Helper function to perform software conversion (as needed) when loading a buffer from Switch 305 * Helper function to perform software conversion (as needed) when loading a buffer from Switch
286 * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with 306 * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
@@ -301,6 +321,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
301 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 321 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
302 ConvertS8Z24ToZ24S8(data, width, height); 322 ConvertS8Z24ToZ24S8(data, width, height);
303 break; 323 break;
324
325 case PixelFormat::G8R8:
326 // Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8.
327 ConvertG8R8ToR8G8(data, width, height);
328 break;
304 } 329 }
305} 330}
306 331
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 1bedae992..718c45ce1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -37,13 +37,15 @@ struct SurfaceParams {
37 DXN1 = 11, // This is also known as BC4 37 DXN1 = 11, // This is also known as BC4
38 BC7U = 12, 38 BC7U = 12,
39 ASTC_2D_4X4 = 13, 39 ASTC_2D_4X4 = 13,
40 G8R8 = 14,
40 41
41 MaxColorFormat, 42 MaxColorFormat,
42 43
43 // DepthStencil formats 44 // DepthStencil formats
44 Z24S8 = 14, 45 Z24S8 = 15,
45 S8Z24 = 15, 46 S8Z24 = 16,
46 Z32F = 16, 47 Z32F = 17,
48 Z16 = 18,
47 49
48 MaxDepthStencilFormat, 50 MaxDepthStencilFormat,
49 51
@@ -95,9 +97,11 @@ struct SurfaceParams {
95 4, // DXN1 97 4, // DXN1
96 4, // BC7U 98 4, // BC7U
97 4, // ASTC_2D_4X4 99 4, // ASTC_2D_4X4
100 1, // G8R8
98 1, // Z24S8 101 1, // Z24S8
99 1, // S8Z24 102 1, // S8Z24
100 1, // Z32F 103 1, // Z32F
104 1, // Z16
101 }}; 105 }};
102 106
103 ASSERT(static_cast<size_t>(format) < compression_factor_table.size()); 107 ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
@@ -123,9 +127,11 @@ struct SurfaceParams {
123 64, // DXN1 127 64, // DXN1
124 128, // BC7U 128 128, // BC7U
125 32, // ASTC_2D_4X4 129 32, // ASTC_2D_4X4
130 16, // G8R8
126 32, // Z24S8 131 32, // Z24S8
127 32, // S8Z24 132 32, // S8Z24
128 32, // Z32F 133 32, // Z32F
134 16, // Z16
129 }}; 135 }};
130 136
131 ASSERT(static_cast<size_t>(format) < bpp_table.size()); 137 ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -143,6 +149,8 @@ struct SurfaceParams {
143 return PixelFormat::Z24S8; 149 return PixelFormat::Z24S8;
144 case Tegra::DepthFormat::Z32_FLOAT: 150 case Tegra::DepthFormat::Z32_FLOAT:
145 return PixelFormat::Z32F; 151 return PixelFormat::Z32F;
152 case Tegra::DepthFormat::Z16_UNORM:
153 return PixelFormat::Z16;
146 default: 154 default:
147 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 155 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
148 UNREACHABLE(); 156 UNREACHABLE();
@@ -181,6 +189,8 @@ struct SurfaceParams {
181 return PixelFormat::A1B5G5R5; 189 return PixelFormat::A1B5G5R5;
182 case Tegra::Texture::TextureFormat::R8: 190 case Tegra::Texture::TextureFormat::R8:
183 return PixelFormat::R8; 191 return PixelFormat::R8;
192 case Tegra::Texture::TextureFormat::G8R8:
193 return PixelFormat::G8R8;
184 case Tegra::Texture::TextureFormat::R16_G16_B16_A16: 194 case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
185 return PixelFormat::RGBA16F; 195 return PixelFormat::RGBA16F;
186 case Tegra::Texture::TextureFormat::BF10GF11RF11: 196 case Tegra::Texture::TextureFormat::BF10GF11RF11:
@@ -218,6 +228,8 @@ struct SurfaceParams {
218 return Tegra::Texture::TextureFormat::A1B5G5R5; 228 return Tegra::Texture::TextureFormat::A1B5G5R5;
219 case PixelFormat::R8: 229 case PixelFormat::R8:
220 return Tegra::Texture::TextureFormat::R8; 230 return Tegra::Texture::TextureFormat::R8;
231 case PixelFormat::G8R8:
232 return Tegra::Texture::TextureFormat::G8R8;
221 case PixelFormat::RGBA16F: 233 case PixelFormat::RGBA16F:
222 return Tegra::Texture::TextureFormat::R16_G16_B16_A16; 234 return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
223 case PixelFormat::R11FG11FB10F: 235 case PixelFormat::R11FG11FB10F:
@@ -249,6 +261,8 @@ struct SurfaceParams {
249 return Tegra::DepthFormat::Z24_S8_UNORM; 261 return Tegra::DepthFormat::Z24_S8_UNORM;
250 case PixelFormat::Z32F: 262 case PixelFormat::Z32F:
251 return Tegra::DepthFormat::Z32_FLOAT; 263 return Tegra::DepthFormat::Z32_FLOAT;
264 case PixelFormat::Z16:
265 return Tegra::DepthFormat::Z16_UNORM;
252 default: 266 default:
253 UNREACHABLE(); 267 UNREACHABLE();
254 } 268 }
@@ -295,6 +309,7 @@ struct SurfaceParams {
295 309
296 static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) { 310 static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
297 switch (format) { 311 switch (format) {
312 case Tegra::DepthFormat::Z16_UNORM:
298 case Tegra::DepthFormat::S8_Z24_UNORM: 313 case Tegra::DepthFormat::S8_Z24_UNORM:
299 case Tegra::DepthFormat::Z24_S8_UNORM: 314 case Tegra::DepthFormat::Z24_S8_UNORM:
300 return ComponentType::UNorm; 315 return ComponentType::UNorm;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 5914077e8..5fae95788 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -42,13 +42,14 @@ enum class ExitMethod {
42struct Subroutine { 42struct Subroutine {
43 /// Generates a name suitable for GLSL source code. 43 /// Generates a name suitable for GLSL source code.
44 std::string GetName() const { 44 std::string GetName() const {
45 return "sub_" + std::to_string(begin) + '_' + std::to_string(end); 45 return "sub_" + std::to_string(begin) + '_' + std::to_string(end) + '_' + suffix;
46 } 46 }
47 47
48 u32 begin; ///< Entry point of the subroutine. 48 u32 begin; ///< Entry point of the subroutine.
49 u32 end; ///< Return point of the subroutine. 49 u32 end; ///< Return point of the subroutine.
50 ExitMethod exit_method; ///< Exit method of the subroutine. 50 const std::string& suffix; ///< Suffix of the shader, used to make a unique subroutine name
51 std::set<u32> labels; ///< Addresses refereced by JMP instructions. 51 ExitMethod exit_method; ///< Exit method of the subroutine.
52 std::set<u32> labels; ///< Addresses refereced by JMP instructions.
52 53
53 bool operator<(const Subroutine& rhs) const { 54 bool operator<(const Subroutine& rhs) const {
54 return std::tie(begin, end) < std::tie(rhs.begin, rhs.end); 55 return std::tie(begin, end) < std::tie(rhs.begin, rhs.end);
@@ -58,11 +59,11 @@ struct Subroutine {
58/// Analyzes shader code and produces a set of subroutines. 59/// Analyzes shader code and produces a set of subroutines.
59class ControlFlowAnalyzer { 60class ControlFlowAnalyzer {
60public: 61public:
61 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset) 62 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix)
62 : program_code(program_code) { 63 : program_code(program_code) {
63 64
64 // Recursively finds all subroutines. 65 // Recursively finds all subroutines.
65 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END); 66 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix);
66 if (program_main.exit_method != ExitMethod::AlwaysEnd) 67 if (program_main.exit_method != ExitMethod::AlwaysEnd)
67 throw DecompileFail("Program does not always end"); 68 throw DecompileFail("Program does not always end");
68 } 69 }
@@ -77,12 +78,12 @@ private:
77 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; 78 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map;
78 79
79 /// Adds and analyzes a new subroutine if it is not added yet. 80 /// Adds and analyzes a new subroutine if it is not added yet.
80 const Subroutine& AddSubroutine(u32 begin, u32 end) { 81 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) {
81 auto iter = subroutines.find(Subroutine{begin, end}); 82 auto iter = subroutines.find(Subroutine{begin, end, suffix});
82 if (iter != subroutines.end()) 83 if (iter != subroutines.end())
83 return *iter; 84 return *iter;
84 85
85 Subroutine subroutine{begin, end}; 86 Subroutine subroutine{begin, end, suffix};
86 subroutine.exit_method = Scan(begin, end, subroutine.labels); 87 subroutine.exit_method = Scan(begin, end, subroutine.labels);
87 if (subroutine.exit_method == ExitMethod::Undetermined) 88 if (subroutine.exit_method == ExitMethod::Undetermined)
88 throw DecompileFail("Recursive function detected"); 89 throw DecompileFail("Recursive function detected");
@@ -191,7 +192,8 @@ public:
191 UnsignedInteger, 192 UnsignedInteger,
192 }; 193 };
193 194
194 GLSLRegister(size_t index, ShaderWriter& shader) : index{index}, shader{shader} {} 195 GLSLRegister(size_t index, ShaderWriter& shader, const std::string& suffix)
196 : index{index}, shader{shader}, suffix{suffix} {}
195 197
196 /// Gets the GLSL type string for a register 198 /// Gets the GLSL type string for a register
197 static std::string GetTypeString(Type type) { 199 static std::string GetTypeString(Type type) {
@@ -216,7 +218,7 @@ public:
216 /// Returns a GLSL string representing the current state of the register 218 /// Returns a GLSL string representing the current state of the register
217 const std::string GetActiveString() { 219 const std::string GetActiveString() {
218 declr_type.insert(active_type); 220 declr_type.insert(active_type);
219 return GetPrefixString(active_type) + std::to_string(index); 221 return GetPrefixString(active_type) + std::to_string(index) + '_' + suffix;
220 } 222 }
221 223
222 /// Returns true if the active type is a float 224 /// Returns true if the active type is a float
@@ -251,6 +253,7 @@ private:
251 ShaderWriter& shader; 253 ShaderWriter& shader;
252 Type active_type{Type::Float}; 254 Type active_type{Type::Float};
253 std::set<Type> declr_type; 255 std::set<Type> declr_type;
256 const std::string& suffix;
254}; 257};
255 258
256/** 259/**
@@ -262,8 +265,8 @@ private:
262class GLSLRegisterManager { 265class GLSLRegisterManager {
263public: 266public:
264 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 267 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
265 const Maxwell3D::Regs::ShaderStage& stage) 268 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix)
266 : shader{shader}, declarations{declarations}, stage{stage} { 269 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} {
267 BuildRegisterList(); 270 BuildRegisterList();
268 } 271 }
269 272
@@ -430,12 +433,12 @@ public:
430 } 433 }
431 434
432 /// Add declarations for registers 435 /// Add declarations for registers
433 void GenerateDeclarations() { 436 void GenerateDeclarations(const std::string& suffix) {
434 for (const auto& reg : regs) { 437 for (const auto& reg : regs) {
435 for (const auto& type : reg.DeclaredTypes()) { 438 for (const auto& type : reg.DeclaredTypes()) {
436 declarations.AddLine(GLSLRegister::GetTypeString(type) + ' ' + 439 declarations.AddLine(GLSLRegister::GetTypeString(type) + ' ' +
437 GLSLRegister::GetPrefixString(type) + 440 reg.GetPrefixString(type) + std::to_string(reg.GetIndex()) +
438 std::to_string(reg.GetIndex()) + " = 0;"); 441 '_' + suffix + " = 0;");
439 } 442 }
440 } 443 }
441 declarations.AddNewLine(); 444 declarations.AddNewLine();
@@ -558,7 +561,7 @@ private:
558 /// Build the GLSL register list. 561 /// Build the GLSL register list.
559 void BuildRegisterList() { 562 void BuildRegisterList() {
560 for (size_t index = 0; index < Register::NumRegisters; ++index) { 563 for (size_t index = 0; index < Register::NumRegisters; ++index) {
561 regs.emplace_back(index, shader); 564 regs.emplace_back(index, shader, suffix);
562 } 565 }
563 } 566 }
564 567
@@ -620,16 +623,17 @@ private:
620 std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; 623 std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
621 std::vector<SamplerEntry> used_samplers; 624 std::vector<SamplerEntry> used_samplers;
622 const Maxwell3D::Regs::ShaderStage& stage; 625 const Maxwell3D::Regs::ShaderStage& stage;
626 const std::string& suffix;
623}; 627};
624 628
625class GLSLGenerator { 629class GLSLGenerator {
626public: 630public:
627 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, 631 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
628 u32 main_offset, Maxwell3D::Regs::ShaderStage stage) 632 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix)
629 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 633 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
630 stage(stage) { 634 stage(stage), suffix(suffix) {
631 635
632 Generate(); 636 Generate(suffix);
633 } 637 }
634 638
635 std::string GetShaderCode() { 639 std::string GetShaderCode() {
@@ -644,7 +648,7 @@ public:
644private: 648private:
645 /// Gets the Subroutine object corresponding to the specified address. 649 /// Gets the Subroutine object corresponding to the specified address.
646 const Subroutine& GetSubroutine(u32 begin, u32 end) const { 650 const Subroutine& GetSubroutine(u32 begin, u32 end) const {
647 auto iter = subroutines.find(Subroutine{begin, end}); 651 auto iter = subroutines.find(Subroutine{begin, end, suffix});
648 ASSERT(iter != subroutines.end()); 652 ASSERT(iter != subroutines.end());
649 return *iter; 653 return *iter;
650 } 654 }
@@ -689,7 +693,7 @@ private:
689 // Can't assign to the constant predicate. 693 // Can't assign to the constant predicate.
690 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); 694 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
691 695
692 std::string variable = 'p' + std::to_string(pred); 696 std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
693 shader.AddLine(variable + " = " + value + ';'); 697 shader.AddLine(variable + " = " + value + ';');
694 declr_predicates.insert(std::move(variable)); 698 declr_predicates.insert(std::move(variable));
695 } 699 }
@@ -707,7 +711,7 @@ private:
707 if (index == static_cast<u64>(Pred::UnusedIndex)) 711 if (index == static_cast<u64>(Pred::UnusedIndex))
708 variable = "true"; 712 variable = "true";
709 else 713 else
710 variable = 'p' + std::to_string(index); 714 variable = 'p' + std::to_string(index) + '_' + suffix;
711 715
712 if (negate) { 716 if (negate) {
713 return "!(" + variable + ')'; 717 return "!(" + variable + ')';
@@ -728,10 +732,10 @@ private:
728 const std::string& op_a, const std::string& op_b) const { 732 const std::string& op_a, const std::string& op_b) const {
729 using Tegra::Shader::PredCondition; 733 using Tegra::Shader::PredCondition;
730 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { 734 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
731 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, 735 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
732 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, 736 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
733 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, 737 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="},
734 {PredCondition::NotEqualWithNan, "!="}, 738 {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="},
735 }; 739 };
736 740
737 const auto& comparison{PredicateComparisonStrings.find(condition)}; 741 const auto& comparison{PredicateComparisonStrings.find(condition)};
@@ -739,7 +743,8 @@ private:
739 "Unknown predicate comparison operation"); 743 "Unknown predicate comparison operation");
740 744
741 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; 745 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'};
742 if (condition == PredCondition::NotEqualWithNan) { 746 if (condition == PredCondition::LessThanWithNan ||
747 condition == PredCondition::NotEqualWithNan) {
743 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; 748 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')';
744 } 749 }
745 750
@@ -968,6 +973,29 @@ private:
968 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); 973 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
969 break; 974 break;
970 } 975 }
976 case OpCode::Id::FADD32I: {
977 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
978 std::string op_b = GetImmediate32(instr);
979
980 if (instr.fadd32i.abs_a) {
981 op_a = "abs(" + op_a + ')';
982 }
983
984 if (instr.fadd32i.negate_a) {
985 op_a = "-(" + op_a + ')';
986 }
987
988 if (instr.fadd32i.abs_b) {
989 op_b = "abs(" + op_b + ')';
990 }
991
992 if (instr.fadd32i.negate_b) {
993 op_b = "-(" + op_b + ')';
994 }
995
996 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1);
997 break;
998 }
971 } 999 }
972 break; 1000 break;
973 } 1001 }
@@ -1616,16 +1644,32 @@ private:
1616 shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';'); 1644 shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';');
1617 } 1645 }
1618 1646
1619 shader.AddLine("return true;"); 1647 switch (instr.flow.cond) {
1620 if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) { 1648 case Tegra::Shader::FlowCondition::Always:
1621 // If this is an unconditional exit then just end processing here, otherwise 1649 shader.AddLine("return true;");
1622 // we have to account for the possibility of the condition not being met, so 1650 if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
1623 // continue processing the next instruction. 1651 // If this is an unconditional exit then just end processing here,
1624 offset = PROGRAM_END - 1; 1652 // otherwise we have to account for the possibility of the condition
1653 // not being met, so continue processing the next instruction.
1654 offset = PROGRAM_END - 1;
1655 }
1656 break;
1657
1658 case Tegra::Shader::FlowCondition::Fcsm_Tr:
1659 // TODO(bunnei): What is this used for? If we assume this conditon is not
1660 // satisifed, dual vertex shaders in Farming Simulator make more sense
1661 LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr");
1662 break;
1663
1664 default:
1665 LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}",
1666 static_cast<u32>(instr.flow.cond.Value()));
1667 UNREACHABLE();
1625 } 1668 }
1626 break; 1669 break;
1627 } 1670 }
1628 case OpCode::Id::KIL: { 1671 case OpCode::Id::KIL: {
1672 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
1629 shader.AddLine("discard;"); 1673 shader.AddLine("discard;");
1630 break; 1674 break;
1631 } 1675 }
@@ -1646,8 +1690,9 @@ private:
1646 // can ignore this when generating GLSL code. 1690 // can ignore this when generating GLSL code.
1647 break; 1691 break;
1648 } 1692 }
1649 case OpCode::Id::DEPBAR: 1693 case OpCode::Id::SYNC:
1650 case OpCode::Id::SYNC: { 1694 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
1695 case OpCode::Id::DEPBAR: {
1651 // TODO(Subv): Find out if we actually have to care about these instructions or if 1696 // TODO(Subv): Find out if we actually have to care about these instructions or if
1652 // the GLSL compiler takes care of that for us. 1697 // the GLSL compiler takes care of that for us.
1653 LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed"); 1698 LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed");
@@ -1687,7 +1732,7 @@ private:
1687 return program_counter; 1732 return program_counter;
1688 } 1733 }
1689 1734
1690 void Generate() { 1735 void Generate(const std::string& suffix) {
1691 // Add declarations for all subroutines 1736 // Add declarations for all subroutines
1692 for (const auto& subroutine : subroutines) { 1737 for (const auto& subroutine : subroutines) {
1693 shader.AddLine("bool " + subroutine.GetName() + "();"); 1738 shader.AddLine("bool " + subroutine.GetName() + "();");
@@ -1695,7 +1740,7 @@ private:
1695 shader.AddNewLine(); 1740 shader.AddNewLine();
1696 1741
1697 // Add the main entry point 1742 // Add the main entry point
1698 shader.AddLine("bool exec_shader() {"); 1743 shader.AddLine("bool exec_" + suffix + "() {");
1699 ++shader.scope; 1744 ++shader.scope;
1700 CallSubroutine(GetSubroutine(main_offset, PROGRAM_END)); 1745 CallSubroutine(GetSubroutine(main_offset, PROGRAM_END));
1701 --shader.scope; 1746 --shader.scope;
@@ -1758,7 +1803,7 @@ private:
1758 1803
1759 /// Add declarations for registers 1804 /// Add declarations for registers
1760 void GenerateDeclarations() { 1805 void GenerateDeclarations() {
1761 regs.GenerateDeclarations(); 1806 regs.GenerateDeclarations(suffix);
1762 1807
1763 for (const auto& pred : declr_predicates) { 1808 for (const auto& pred : declr_predicates) {
1764 declarations.AddLine("bool " + pred + " = false;"); 1809 declarations.AddLine("bool " + pred + " = false;");
@@ -1771,27 +1816,30 @@ private:
1771 const ProgramCode& program_code; 1816 const ProgramCode& program_code;
1772 const u32 main_offset; 1817 const u32 main_offset;
1773 Maxwell3D::Regs::ShaderStage stage; 1818 Maxwell3D::Regs::ShaderStage stage;
1819 const std::string& suffix;
1774 1820
1775 ShaderWriter shader; 1821 ShaderWriter shader;
1776 ShaderWriter declarations; 1822 ShaderWriter declarations;
1777 GLSLRegisterManager regs{shader, declarations, stage}; 1823 GLSLRegisterManager regs{shader, declarations, stage, suffix};
1778 1824
1779 // Declarations 1825 // Declarations
1780 std::set<std::string> declr_predicates; 1826 std::set<std::string> declr_predicates;
1781}; // namespace Decompiler 1827}; // namespace Decompiler
1782 1828
1783std::string GetCommonDeclarations() { 1829std::string GetCommonDeclarations() {
1784 std::string declarations = "bool exec_shader();\n"; 1830 std::string declarations;
1785 declarations += "#define MAX_CONSTBUFFER_ELEMENTS " + 1831 declarations += "#define MAX_CONSTBUFFER_ELEMENTS " +
1786 std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4))); 1832 std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4)));
1833 declarations += '\n';
1787 return declarations; 1834 return declarations;
1788} 1835}
1789 1836
1790boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 1837boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
1791 Maxwell3D::Regs::ShaderStage stage) { 1838 Maxwell3D::Regs::ShaderStage stage,
1839 const std::string& suffix) {
1792 try { 1840 try {
1793 auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines(); 1841 auto subroutines = ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
1794 GLSLGenerator generator(subroutines, program_code, main_offset, stage); 1842 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix);
1795 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; 1843 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
1796 } catch (const DecompileFail& exception) { 1844 } catch (const DecompileFail& exception) {
1797 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 1845 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 382c76b7a..7610dad3a 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -20,7 +20,8 @@ using Tegra::Engines::Maxwell3D;
20std::string GetCommonDeclarations(); 20std::string GetCommonDeclarations();
21 21
22boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 22boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
23 Maxwell3D::Regs::ShaderStage stage); 23 Maxwell3D::Regs::ShaderStage stage,
24 const std::string& suffix);
24 25
25} // namespace Decompiler 26} // namespace Decompiler
26} // namespace GLShader 27} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index c1e6fac9f..129c777d1 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -17,10 +17,17 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConf
17 std::string out = "#version 430 core\n"; 17 std::string out = "#version 430 core\n";
18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 out += "bool exec_vertex();\n";
21
22 if (setup.IsDualProgram()) {
23 out += "bool exec_vertex_b();\n";
24 }
25
26 ProgramResult program =
27 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
28 Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
29 .get_value_or({});
20 30
21 ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
22 Maxwell3D::Regs::ShaderStage::Vertex)
23 .get_value_or({});
24 out += R"( 31 out += R"(
25 32
26out gl_PerVertex { 33out gl_PerVertex {
@@ -34,7 +41,14 @@ layout (std140) uniform vs_config {
34}; 41};
35 42
36void main() { 43void main() {
37 exec_shader(); 44 exec_vertex();
45)";
46
47 if (setup.IsDualProgram()) {
48 out += " exec_vertex_b();";
49 }
50
51 out += R"(
38 52
39 // Viewport can be flipped, which is unsupported by glViewport 53 // Viewport can be flipped, which is unsupported by glViewport
40 position.xy *= viewport_flip.xy; 54 position.xy *= viewport_flip.xy;
@@ -44,8 +58,19 @@ void main() {
44 // For now, this is here to bring order in lieu of proper emulation 58 // For now, this is here to bring order in lieu of proper emulation
45 position.w = 1.0; 59 position.w = 1.0;
46} 60}
61
47)"; 62)";
63
48 out += program.first; 64 out += program.first;
65
66 if (setup.IsDualProgram()) {
67 ProgramResult program_b =
68 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
69 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
70 .get_value_or({});
71 out += program_b.first;
72 }
73
49 return {out, program.second}; 74 return {out, program.second};
50} 75}
51 76
@@ -53,12 +78,13 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSCo
53 std::string out = "#version 430 core\n"; 78 std::string out = "#version 430 core\n";
54 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 79 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
55 out += Decompiler::GetCommonDeclarations(); 80 out += Decompiler::GetCommonDeclarations();
81 out += "bool exec_fragment();\n";
56 82
57 ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET, 83 ProgramResult program =
58 Maxwell3D::Regs::ShaderStage::Fragment) 84 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
59 .get_value_or({}); 85 Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
86 .get_value_or({});
60 out += R"( 87 out += R"(
61
62in vec4 position; 88in vec4 position;
63out vec4 color; 89out vec4 color;
64 90
@@ -67,7 +93,7 @@ layout (std140) uniform fs_config {
67}; 93};
68 94
69void main() { 95void main() {
70 exec_shader(); 96 exec_fragment();
71} 97}
72 98
73)"; 99)";
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index ed890e0f9..4729ce0fc 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -115,21 +115,48 @@ struct ShaderEntries {
115using ProgramResult = std::pair<std::string, ShaderEntries>; 115using ProgramResult = std::pair<std::string, ShaderEntries>;
116 116
117struct ShaderSetup { 117struct ShaderSetup {
118 ShaderSetup(ProgramCode&& program_code) : program_code(std::move(program_code)) {} 118 ShaderSetup(const ProgramCode& program_code) {
119 program.code = program_code;
120 }
121
122 struct {
123 ProgramCode code;
124 ProgramCode code_b; // Used for dual vertex shaders
125 } program;
119 126
120 ProgramCode program_code;
121 bool program_code_hash_dirty = true; 127 bool program_code_hash_dirty = true;
122 128
123 u64 GetProgramCodeHash() { 129 u64 GetProgramCodeHash() {
124 if (program_code_hash_dirty) { 130 if (program_code_hash_dirty) {
125 program_code_hash = Common::ComputeHash64(&program_code, sizeof(program_code)); 131 program_code_hash = GetNewHash();
126 program_code_hash_dirty = false; 132 program_code_hash_dirty = false;
127 } 133 }
128 return program_code_hash; 134 return program_code_hash;
129 } 135 }
130 136
137 /// Used in scenarios where we have a dual vertex shaders
138 void SetProgramB(const ProgramCode& program_b) {
139 program.code_b = program_b;
140 has_program_b = true;
141 }
142
143 bool IsDualProgram() const {
144 return has_program_b;
145 }
146
131private: 147private:
148 u64 GetNewHash() const {
149 if (has_program_b) {
150 // Compute hash over dual shader programs
151 return Common::ComputeHash64(&program, sizeof(program));
152 } else {
153 // Compute hash over a single shader program
154 return Common::ComputeHash64(&program.code, program.code.size());
155 }
156 }
157
132 u64 program_code_hash{}; 158 u64 program_code_hash{};
159 bool has_program_b{};
133}; 160};
134 161
135struct MaxwellShaderConfigCommon { 162struct MaxwellShaderConfigCommon {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 00841e937..1930fa6ef 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -92,11 +92,24 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
92 return matrix; 92 return matrix;
93} 93}
94 94
95ScopeAcquireGLContext::ScopeAcquireGLContext() {
96 if (Settings::values.use_multi_core) {
97 VideoCore::g_emu_window->MakeCurrent();
98 }
99}
100ScopeAcquireGLContext::~ScopeAcquireGLContext() {
101 if (Settings::values.use_multi_core) {
102 VideoCore::g_emu_window->DoneCurrent();
103 }
104}
105
95RendererOpenGL::RendererOpenGL() = default; 106RendererOpenGL::RendererOpenGL() = default;
96RendererOpenGL::~RendererOpenGL() = default; 107RendererOpenGL::~RendererOpenGL() = default;
97 108
98/// Swap buffers (render frame) 109/// Swap buffers (render frame)
99void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { 110void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) {
111 ScopeAcquireGLContext acquire_context;
112
100 Core::System::GetInstance().perf_stats.EndSystemFrame(); 113 Core::System::GetInstance().perf_stats.EndSystemFrame();
101 114
102 // Maintain the rasterizer's state as a priority 115 // Maintain the rasterizer's state as a priority
@@ -418,7 +431,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
418 431
419/// Initialize the renderer 432/// Initialize the renderer
420bool RendererOpenGL::Init() { 433bool RendererOpenGL::Init() {
421 render_window->MakeCurrent(); 434 ScopeAcquireGLContext acquire_context;
422 435
423 if (GLAD_GL_KHR_debug) { 436 if (GLAD_GL_KHR_debug) {
424 glEnable(GL_DEBUG_OUTPUT); 437 glEnable(GL_DEBUG_OUTPUT);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 21f0d298c..fd0267cf5 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -31,6 +31,13 @@ struct ScreenInfo {
31 TextureInfo texture; 31 TextureInfo texture;
32}; 32};
33 33
34/// Helper class to acquire/release OpenGL context within a given scope
35class ScopeAcquireGLContext : NonCopyable {
36public:
37 ScopeAcquireGLContext();
38 ~ScopeAcquireGLContext();
39};
40
34class RendererOpenGL : public RendererBase { 41class RendererOpenGL : public RendererBase {
35public: 42public:
36 RendererOpenGL(); 43 RendererOpenGL();
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index b3937b2fe..be18aa299 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -62,6 +62,7 @@ u32 BytesPerPixel(TextureFormat format) {
62 return 4; 62 return 4;
63 case TextureFormat::A1B5G5R5: 63 case TextureFormat::A1B5G5R5:
64 case TextureFormat::B5G6R5: 64 case TextureFormat::B5G6R5:
65 case TextureFormat::G8R8:
65 return 2; 66 return 2;
66 case TextureFormat::R8: 67 case TextureFormat::R8:
67 return 1; 68 return 1;
@@ -77,6 +78,8 @@ u32 BytesPerPixel(TextureFormat format) {
77 78
78static u32 DepthBytesPerPixel(DepthFormat format) { 79static u32 DepthBytesPerPixel(DepthFormat format) {
79 switch (format) { 80 switch (format) {
81 case DepthFormat::Z16_UNORM:
82 return 2;
80 case DepthFormat::S8_Z24_UNORM: 83 case DepthFormat::S8_Z24_UNORM:
81 case DepthFormat::Z24_S8_UNORM: 84 case DepthFormat::Z24_S8_UNORM:
82 case DepthFormat::Z32_FLOAT: 85 case DepthFormat::Z32_FLOAT:
@@ -110,6 +113,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
110 case TextureFormat::A1B5G5R5: 113 case TextureFormat::A1B5G5R5:
111 case TextureFormat::B5G6R5: 114 case TextureFormat::B5G6R5:
112 case TextureFormat::R8: 115 case TextureFormat::R8:
116 case TextureFormat::G8R8:
113 case TextureFormat::R16_G16_B16_A16: 117 case TextureFormat::R16_G16_B16_A16:
114 case TextureFormat::R32_G32_B32_A32: 118 case TextureFormat::R32_G32_B32_A32:
115 case TextureFormat::BF10GF11RF11: 119 case TextureFormat::BF10GF11RF11:
@@ -133,6 +137,7 @@ std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 wid
133 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 137 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
134 138
135 switch (format) { 139 switch (format) {
140 case DepthFormat::Z16_UNORM:
136 case DepthFormat::S8_Z24_UNORM: 141 case DepthFormat::S8_Z24_UNORM:
137 case DepthFormat::Z24_S8_UNORM: 142 case DepthFormat::Z24_S8_UNORM:
138 case DepthFormat::Z32_FLOAT: 143 case DepthFormat::Z32_FLOAT:
@@ -164,6 +169,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
164 case TextureFormat::A1B5G5R5: 169 case TextureFormat::A1B5G5R5:
165 case TextureFormat::B5G6R5: 170 case TextureFormat::B5G6R5:
166 case TextureFormat::R8: 171 case TextureFormat::R8:
172 case TextureFormat::G8R8:
167 case TextureFormat::BF10GF11RF11: 173 case TextureFormat::BF10GF11RF11:
168 case TextureFormat::R32_G32_B32_A32: 174 case TextureFormat::R32_G32_B32_A32:
169 // TODO(Subv): For the time being just forward the same data without any decoding. 175 // TODO(Subv): For the time being just forward the same data without any decoding.
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 833085559..159b2c32b 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -20,7 +20,10 @@
20EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 20EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
21 21
22void EmuThread::run() { 22void EmuThread::run() {
23 render_window->MakeCurrent(); 23 if (!Settings::values.use_multi_core) {
24 // Single core mode must acquire OpenGL context for entire emulation session
25 render_window->MakeCurrent();
26 }
24 27
25 MicroProfileOnThreadCreate("EmuThread"); 28 MicroProfileOnThreadCreate("EmuThread");
26 29
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9ce8d7c27..16812e077 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -374,6 +374,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
374 374
375 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())}; 375 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
376 376
377 render_window->DoneCurrent();
378
377 if (result != Core::System::ResultStatus::Success) { 379 if (result != Core::System::ResultStatus::Success) {
378 switch (result) { 380 switch (result) {
379 case Core::System::ResultStatus::ErrorGetLoader: 381 case Core::System::ResultStatus::ErrorGetLoader:
@@ -916,6 +918,7 @@ int main(int argc, char* argv[]) {
916 QCoreApplication::setApplicationName("yuzu"); 918 QCoreApplication::setApplicationName("yuzu");
917 919
918 QApplication::setAttribute(Qt::AA_X11InitThreads); 920 QApplication::setAttribute(Qt::AA_X11InitThreads);
921 QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
919 QApplication app(argc, argv); 922 QApplication app(argc, argv);
920 923
921 // Qt changes the locale and causes issues in float conversion using std::to_string() when 924 // Qt changes the locale and causes issues in float conversion using std::to_string() when
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e6f0bbe8f..ec73f08bd 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -126,7 +126,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
126 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); 126 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
127 127
128 if (render_window == nullptr) { 128 if (render_window == nullptr) {
129 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); 129 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError());
130 exit(1); 130 exit(1);
131 } 131 }
132 132
@@ -137,12 +137,12 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
137 gl_context = SDL_GL_CreateContext(render_window); 137 gl_context = SDL_GL_CreateContext(render_window);
138 138
139 if (gl_context == nullptr) { 139 if (gl_context == nullptr) {
140 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting..."); 140 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError());
141 exit(1); 141 exit(1);
142 } 142 }
143 143
144 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { 144 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
145 LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting..."); 145 LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
146 exit(1); 146 exit(1);
147 } 147 }
148 148
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 5f67ae4ee..24db1065a 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -22,10 +22,8 @@
22#include "yuzu_cmd/config.h" 22#include "yuzu_cmd/config.h"
23#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 23#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
24 24
25#ifdef _MSC_VER
26#include <getopt.h>
27#else
28#include <getopt.h> 25#include <getopt.h>
26#ifndef _MSC_VER
29#include <unistd.h> 27#include <unistd.h>
30#endif 28#endif
31 29
@@ -150,6 +148,11 @@ int main(int argc, char** argv) {
150 148
151 std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)}; 149 std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)};
152 150
151 if (!Settings::values.use_multi_core) {
152 // Single core mode must acquire OpenGL context for entire emulation session
153 emu_window->MakeCurrent();
154 }
155
153 Core::System& system{Core::System::GetInstance()}; 156 Core::System& system{Core::System::GetInstance()};
154 157
155 SCOPE_EXIT({ system.Shutdown(); }); 158 SCOPE_EXIT({ system.Shutdown(); });