diff options
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 | ||
| 60 | ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { | 60 | ResultCode 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; | |||
| 61 | const u32 MSG_WAITALL = 8; | 61 | const u32 MSG_WAITALL = 8; |
| 62 | #endif | 62 | #endif |
| 63 | 63 | ||
| 64 | const u32 X30_REGISTER = 30; | 64 | const u32 LR_REGISTER = 30; |
| 65 | const u32 SP_REGISTER = 31; | 65 | const u32 SP_REGISTER = 31; |
| 66 | const u32 PC_REGISTER = 32; | 66 | const u32 PC_REGISTER = 32; |
| 67 | const u32 CPSR_REGISTER = 33; | 67 | const u32 CPSR_REGISTER = 33; |
| 68 | const u32 UC_ARM64_REG_Q0 = 34; | ||
| 69 | const u32 FPSCR_REGISTER = 66; | ||
| 70 | |||
| 71 | // TODO/WiP - Used while working on support for FPU | ||
| 72 | const u32 TODO_DUMMY_REG_997 = 997; | ||
| 73 | const 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; | |||
| 144 | static bool memory_break = false; | 152 | static bool memory_break = false; |
| 145 | 153 | ||
| 146 | static Kernel::Thread* current_thread = nullptr; | 154 | static Kernel::Thread* current_thread = nullptr; |
| 155 | static 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; | |||
| 171 | static std::map<u64, Breakpoint> breakpoints_read; | 180 | static std::map<u64, Breakpoint> breakpoints_read; |
| 172 | static std::map<u64, Breakpoint> breakpoints_write; | 181 | static std::map<u64, Breakpoint> breakpoints_write; |
| 173 | 182 | ||
| 183 | struct Module { | ||
| 184 | std::string name; | ||
| 185 | PAddr beg; | ||
| 186 | PAddr end; | ||
| 187 | }; | ||
| 188 | |||
| 189 | static std::vector<Module> modules; | ||
| 190 | |||
| 191 | void 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 | |||
| 174 | static Kernel::Thread* FindThreadById(int id) { | 204 | static 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. |
| 563 | static void HandleSetThread() { | 626 | static 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. |
| 587 | static void HandleThreadAlive() { | 646 | static 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 | ||
| 842 | void Break(bool is_memory_break) { | 938 | void 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. |
| 852 | static void Step() { | 945 | static 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 | ||
| 1193 | void SendTrap(Kernel::Thread* thread, int trap) { | 1293 | void 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. |
| 52 | bool IsConnected(); | 53 | bool IsConnected(); |
| 53 | 54 | ||
| 55 | /// Register module. | ||
| 56 | void 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 | */ |
| 81 | bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); | 85 | bool 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. |
| 84 | bool GetCpuHaltFlag(); | 88 | bool 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. |
| 87 | bool GetCpuStepFlag(); | 91 | bool 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 | ||
| 166 | void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { | 166 | void 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 | ||
| 182 | void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { | 182 | void 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 | ||
| 198 | AudOutU::AudOutU() : ServiceFramework("audout:u") { | 198 | AudOutU::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: | |||
| 22 | private: | 22 | private: |
| 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 | ||
| 153 | IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { | 171 | IGeneralService::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) { | |||
| 19 | void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { | 19 | void 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 | ||
| 28 | void BSD::Socket(Kernel::HLERequestContext& ctx) { | 27 | void 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 | ||
| 401 | bool 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 | |||
| 422 | void Maxwell3D::ProcessClearBuffers() { | 401 | void 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 | |||
| 786 | private: | 791 | private: |
| 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 | ||
| 205 | enum class FlowCondition : u64 { | ||
| 206 | Always = 0xF, | ||
| 207 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? | ||
| 208 | }; | ||
| 209 | |||
| 204 | union Instruction { | 210 | union 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 | ||
| 26 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 28 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 27 | using PixelFormat = SurfaceParams::PixelFormat; | 29 | using 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 | ||
| 186 | static 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 | |||
| 184 | void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | 199 | void 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 | ||
| 117 | static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { | 120 | static 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 | ||
| 201 | static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | 205 | static 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 | |||
| 292 | static 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 { | |||
| 42 | struct Subroutine { | 42 | struct 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. |
| 59 | class ControlFlowAnalyzer { | 60 | class ControlFlowAnalyzer { |
| 60 | public: | 61 | public: |
| 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: | |||
| 262 | class GLSLRegisterManager { | 265 | class GLSLRegisterManager { |
| 263 | public: | 266 | public: |
| 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 | ||
| 625 | class GLSLGenerator { | 629 | class GLSLGenerator { |
| 626 | public: | 630 | public: |
| 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: | |||
| 644 | private: | 648 | private: |
| 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 | ||
| 1783 | std::string GetCommonDeclarations() { | 1829 | std::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 | ||
| 1790 | boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, | 1837 | boost::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; | |||
| 20 | std::string GetCommonDeclarations(); | 20 | std::string GetCommonDeclarations(); |
| 21 | 21 | ||
| 22 | boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, | 22 | boost::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 | ||
| 26 | out gl_PerVertex { | 33 | out gl_PerVertex { |
| @@ -34,7 +41,14 @@ layout (std140) uniform vs_config { | |||
| 34 | }; | 41 | }; |
| 35 | 42 | ||
| 36 | void main() { | 43 | void 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 | |||
| 62 | in vec4 position; | 88 | in vec4 position; |
| 63 | out vec4 color; | 89 | out vec4 color; |
| 64 | 90 | ||
| @@ -67,7 +93,7 @@ layout (std140) uniform fs_config { | |||
| 67 | }; | 93 | }; |
| 68 | 94 | ||
| 69 | void main() { | 95 | void 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 { | |||
| 115 | using ProgramResult = std::pair<std::string, ShaderEntries>; | 115 | using ProgramResult = std::pair<std::string, ShaderEntries>; |
| 116 | 116 | ||
| 117 | struct ShaderSetup { | 117 | struct 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 | |||
| 131 | private: | 147 | private: |
| 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 | ||
| 135 | struct MaxwellShaderConfigCommon { | 162 | struct 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 | ||
| 95 | ScopeAcquireGLContext::ScopeAcquireGLContext() { | ||
| 96 | if (Settings::values.use_multi_core) { | ||
| 97 | VideoCore::g_emu_window->MakeCurrent(); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | ScopeAcquireGLContext::~ScopeAcquireGLContext() { | ||
| 101 | if (Settings::values.use_multi_core) { | ||
| 102 | VideoCore::g_emu_window->DoneCurrent(); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 95 | RendererOpenGL::RendererOpenGL() = default; | 106 | RendererOpenGL::RendererOpenGL() = default; |
| 96 | RendererOpenGL::~RendererOpenGL() = default; | 107 | RendererOpenGL::~RendererOpenGL() = default; |
| 97 | 108 | ||
| 98 | /// Swap buffers (render frame) | 109 | /// Swap buffers (render frame) |
| 99 | void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { | 110 | void 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 |
| 420 | bool RendererOpenGL::Init() { | 433 | bool 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 | ||
| 35 | class ScopeAcquireGLContext : NonCopyable { | ||
| 36 | public: | ||
| 37 | ScopeAcquireGLContext(); | ||
| 38 | ~ScopeAcquireGLContext(); | ||
| 39 | }; | ||
| 40 | |||
| 34 | class RendererOpenGL : public RendererBase { | 41 | class RendererOpenGL : public RendererBase { |
| 35 | public: | 42 | public: |
| 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 | ||
| 78 | static u32 DepthBytesPerPixel(DepthFormat format) { | 79 | static 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 @@ | |||
| 20 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 20 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} |
| 21 | 21 | ||
| 22 | void EmuThread::run() { | 22 | void 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(); }); |