diff options
Diffstat (limited to 'src')
30 files changed, 976 insertions, 242 deletions
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index c0cc62f03..ce6c5616d 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -35,6 +35,17 @@ LoadDll LoadDll::g_load_dll; } \ } while (0) +static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) { + GDBStub::BreakpointAddress bkpt = + GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute); + if (GDBStub::IsMemoryBreak() || + (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) { + auto core = static_cast<ARM_Unicorn*>(user_data); + core->RecordBreak(bkpt); + uc_emu_stop(uc); + } +} + static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { u32 esr{}; CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); @@ -67,6 +78,10 @@ ARM_Unicorn::ARM_Unicorn() { uc_hook hook{}; CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1)); CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1)); + if (GDBStub::IsServerEnabled()) { + CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1)); + last_bkpt_hit = false; + } } ARM_Unicorn::~ARM_Unicorn() { @@ -155,7 +170,11 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) { } void ARM_Unicorn::Run() { - ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); + if (GDBStub::IsServerEnabled()) { + ExecuteInstructions(std::max(4000000, 0)); + } else { + ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); + } } void ARM_Unicorn::Step() { @@ -168,6 +187,18 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit); CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); CoreTiming::AddTicks(num_instructions); + if (GDBStub::IsServerEnabled()) { + if (last_bkpt_hit) { + uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); + } + Kernel::Thread* thread = Kernel::GetCurrentThread(); + SaveContext(thread->context); + if (last_bkpt_hit) { + last_bkpt_hit = false; + GDBStub::Break(); + } + GDBStub::SendTrap(thread, 5); + } } void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) { @@ -233,3 +264,8 @@ void ARM_Unicorn::PrepareReschedule() { } void ARM_Unicorn::ClearInstructionCache() {} + +void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) { + last_bkpt = bkpt; + last_bkpt_hit = true; +} diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index b99b58e4c..a482a2aa3 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -7,6 +7,7 @@ #include <unicorn/unicorn.h> #include "common/common_types.h" #include "core/arm/arm_interface.h" +#include "core/gdbstub/gdbstub.h" class ARM_Unicorn final : public ARM_Interface { public: @@ -35,7 +36,10 @@ public: void Step() override; void ClearInstructionCache() override; void PageTableChanged() override{}; + void RecordBreak(GDBStub::BreakpointAddress bkpt); private: uc_engine* uc{}; + GDBStub::BreakpointAddress last_bkpt{}; + bool last_bkpt_hit; }; diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 6c5a40ba8..2603192fe 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -32,9 +32,13 @@ #include "common/logging/log.h" #include "common/string_util.h" +#include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/core_cpu.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -137,15 +141,17 @@ static u8 command_buffer[GDB_BUFFER_SIZE]; static u32 command_length; static u32 latest_signal = 0; -static bool step_break = false; static bool memory_break = false; +static Kernel::Thread* current_thread = nullptr; + // Binding to a port within the reserved ports range (0-1023) requires root permissions, // so default to a port outside of that range. static u16 gdbstub_port = 24689; static bool halt_loop = true; static bool step_loop = false; +static bool send_trap = false; // If set to false, the server will never be started and no // gdbstub-related functions will be executed. @@ -165,6 +171,53 @@ static std::map<u64, Breakpoint> breakpoints_execute; static std::map<u64, Breakpoint> breakpoints_read; static std::map<u64, Breakpoint> breakpoints_write; +static Kernel::Thread* FindThreadById(int id) { + for (int core = 0; core < Core::NUM_CPU_CORES; core++) { + auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); + for (auto thread : threads) { + if (thread->GetThreadId() == id) { + current_thread = thread.get(); + return current_thread; + } + } + } + return nullptr; +} + +static u64 RegRead(int id, Kernel::Thread* thread = nullptr) { + if (!thread) { + return 0; + } + + if (id < SP_REGISTER) { + return thread->context.cpu_registers[id]; + } else if (id == SP_REGISTER) { + return thread->context.sp; + } else if (id == PC_REGISTER) { + return thread->context.pc; + } else if (id == CPSR_REGISTER) { + return thread->context.cpsr; + } else { + return 0; + } +} + +static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) { + if (!thread) { + return; + } + + if (id < SP_REGISTER) { + thread->context.cpu_registers[id] = val; + } else if (id == SP_REGISTER) { + thread->context.sp = val; + } else if (id == PC_REGISTER) { + thread->context.pc = val; + } else if (id == CPSR_REGISTER) { + thread->context.cpsr = val; + } +} + /** * Turns hex string character into the equivalent byte. * @@ -193,7 +246,7 @@ static u8 NibbleToHex(u8 n) { if (n < 0xA) { return '0' + n; } else { - return 'A' + n - 0xA; + return 'a' + n - 0xA; } } @@ -439,6 +492,8 @@ static void SendReply(const char* reply) { return; } + NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); + memset(command_buffer, 0, sizeof(command_buffer)); command_length = static_cast<u32>(strlen(reply)); @@ -483,6 +538,22 @@ static void HandleQuery() { } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { SendReply(target_xml); + } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { + std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR); + SendReply(buffer.c_str()); + } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { + std::string val = "m"; + for (int core = 0; core < Core::NUM_CPU_CORES; core++) { + auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); + for (auto thread : threads) { + val += fmt::format("{:x}", thread->GetThreadId()); + val += ","; + } + } + val.pop_back(); + SendReply(val.c_str()); + } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) { + SendReply("l"); } else { SendReply(""); } @@ -490,11 +561,40 @@ static void HandleQuery() { /// Handle set thread command from gdb client. static void HandleSetThread() { - if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 || - memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) { - return SendReply("OK"); + if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) { + int thread_id = -1; + if (command_buffer[2] != '-') { + thread_id = static_cast<int>(HexToInt( + command_buffer + 2, + command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/)); + } + if (thread_id >= 1) { + current_thread = FindThreadById(thread_id); + } + if (!current_thread) { + thread_id = 1; + current_thread = FindThreadById(thread_id); + } + if (current_thread) { + SendReply("OK"); + return; + } } + SendReply("E01"); +} +/// Handle thread alive command from gdb client. +static void HandleThreadAlive() { + int thread_id = static_cast<int>( + HexToInt(command_buffer + 1, + command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/)); + if (thread_id == 0) { + thread_id = 1; + } + if (FindThreadById(thread_id)) { + SendReply("OK"); + return; + } SendReply("E01"); } @@ -503,15 +603,24 @@ static void HandleSetThread() { * * @param signal Signal to be sent to client. */ -static void SendSignal(u32 signal) { +static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { if (gdbserver_socket == -1) { return; } latest_signal = signal; - std::string buffer = fmt::format("T{:02x}", latest_signal); - NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer); + std::string buffer; + if (full) { + buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER, + Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, + Common::swap64(RegRead(SP_REGISTER, thread))); + } else { + buffer = fmt::format("T{:02x};", latest_signal); + } + + buffer += fmt::format("thread:{:x};", thread->GetThreadId()); + SendReply(buffer.c_str()); } @@ -527,7 +636,7 @@ static void ReadCommand() { } else if (c == 0x03) { NGLOG_INFO(Debug_GDBStub, "gdb: found break command"); halt_loop = true; - SendSignal(SIGTRAP); + SendSignal(current_thread, SIGTRAP); return; } else if (c != GDB_STUB_START) { NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); @@ -598,11 +707,11 @@ static void ReadRegister() { } if (id <= SP_REGISTER) { - LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast<int>(id))); + LongToGdbHex(reply, RegRead(id, current_thread)); } else if (id == PC_REGISTER) { - LongToGdbHex(reply, Core::CurrentArmInterface().GetPC()); + LongToGdbHex(reply, RegRead(id, current_thread)); } else if (id == CPSR_REGISTER) { - IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR()); + IntToGdbHex(reply, (u32)RegRead(id, current_thread)); } else { return SendReply("E01"); } @@ -618,16 +727,16 @@ static void ReadRegisters() { u8* bufptr = buffer; for (int reg = 0; reg <= SP_REGISTER; reg++) { - LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg)); + LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); } bufptr += (32 * 16); - LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC()); + LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread)); bufptr += 16; - IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR()); + IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); bufptr += 8; @@ -646,11 +755,11 @@ static void WriteRegister() { } if (id <= SP_REGISTER) { - Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr)); + RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); } else if (id == PC_REGISTER) { - Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr)); + RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); } else if (id == CPSR_REGISTER) { - Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr)); + RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); } else { return SendReply("E01"); } @@ -667,11 +776,11 @@ static void WriteRegisters() { for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { if (reg <= SP_REGISTER) { - Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16)); + RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); } else if (reg == PC_REGISTER) { - Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16)); + RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); } else if (reg == CPSR_REGISTER) { - Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16)); + RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); } else { UNIMPLEMENTED(); } @@ -734,7 +843,7 @@ static void WriteMemory() { void Break(bool is_memory_break) { if (!halt_loop) { halt_loop = true; - SendSignal(SIGTRAP); + send_trap = true; } memory_break = is_memory_break; @@ -744,10 +853,10 @@ void Break(bool is_memory_break) { static void Step() { step_loop = true; halt_loop = true; - step_break = true; - SendSignal(SIGTRAP); + send_trap = true; } +/// Tell the CPU if we hit a memory breakpoint. bool IsMemoryBreak() { if (IsConnected()) { return false; @@ -759,7 +868,6 @@ bool IsMemoryBreak() { /// Tell the CPU to continue executing. static void Continue() { memory_break = false; - step_break = false; step_loop = false; halt_loop = false; } @@ -898,7 +1006,7 @@ void HandlePacket() { HandleSetThread(); break; case '?': - SendSignal(latest_signal); + SendSignal(current_thread, latest_signal); break; case 'k': Shutdown(); @@ -935,6 +1043,9 @@ void HandlePacket() { case 'Z': AddBreakpoint(); break; + case 'T': + HandleThreadAlive(); + break; default: SendReply(""); break; @@ -1079,4 +1190,11 @@ bool GetCpuStepFlag() { void SetCpuStepFlag(bool is_step) { step_loop = is_step; } + +void SendTrap(Kernel::Thread* thread, int trap) { + if (send_trap) { + send_trap = false; + SendSignal(thread, trap); + } +} }; // namespace GDBStub diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index 201fca095..f2418c9e4 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -7,6 +7,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/kernel/thread.h" namespace GDBStub { @@ -91,4 +92,12 @@ bool GetCpuStepFlag(); * @param is_step */ void SetCpuStepFlag(bool is_step); + +/** + * Send trap signal from thread back to the gdbstub server. + * + * @param thread Sending thread. + * @param trap Trap no. + */ +void SendTrap(Kernel::Thread* thread, int trap); } // namespace GDBStub diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 12954556d..b8d6b8d4d 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -561,7 +561,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {32, nullptr, "BeginBlockingHomeButton"}, {33, nullptr, "EndBlockingHomeButton"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, - {50, nullptr, "GetPseudoDeviceId"}, + {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, {65, nullptr, "IsGamePlayRecordingSupported"}, {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, @@ -684,6 +684,17 @@ void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_AM, "(STUBBED) called"); } +void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + + // Returns a 128-bit UUID + rb.Push<u64>(0); + rb.Push<u64>(0); + + NGLOG_WARNING(Service_AM, "(STUBBED) called"); +} + void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 301a6c798..1da79fd01 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -138,6 +138,7 @@ private: void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx); void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); void NotifyRunning(Kernel::HLERequestContext& ctx); + void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 2af4465de..2a9f84037 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -4,6 +4,8 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/service/hid/hid.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" @@ -18,7 +20,7 @@ public: static const FunctionInfo functions[] = { {0, &IUser::Initialize, "Initialize"}, {1, nullptr, "Finalize"}, - {2, nullptr, "ListDevices"}, + {2, &IUser::ListDevices, "ListDevices"}, {3, nullptr, "StartDetection"}, {4, nullptr, "StopDetection"}, {5, nullptr, "Mount"}, @@ -33,24 +35,116 @@ public: {14, nullptr, "GetRegisterInfo"}, {15, nullptr, "GetCommonInfo"}, {16, nullptr, "GetModelInfo"}, - {17, nullptr, "AttachActivateEvent"}, - {18, nullptr, "AttachDeactivateEvent"}, - {19, nullptr, "GetState"}, - {20, nullptr, "GetDeviceState"}, - {21, nullptr, "GetNpadId"}, + {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, + {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {19, &IUser::GetState, "GetState"}, + {20, &IUser::GetDeviceState, "GetDeviceState"}, + {21, &IUser::GetNpadId, "GetNpadId"}, {22, nullptr, "GetApplicationArea2"}, - {23, nullptr, "AttachAvailabilityChangeEvent"}, + {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, {24, nullptr, "RecreateApplicationArea"}, }; RegisterHandlers(functions); + + activate_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:ActivateEvent"); + deactivate_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); + availability_change_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent"); } private: + enum class State : u32 { + NonInitialized = 0, + Initialized = 1, + }; + + enum class DeviceState : u32 { + Initialized = 0, + }; + void Initialize(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NFP, "(STUBBED) called"); + + state = State::Initialized; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + void ListDevices(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 array_size = rp.Pop<u32>(); + + ctx.WriteBuffer(&device_handle, sizeof(device_handle)); + + NGLOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + + void AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 dev_handle = rp.Pop<u64>(); + NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(activate_event); + } + + void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 dev_handle = rp.Pop<u64>(); + NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(deactivate_event); + } + + void GetState(Kernel::HLERequestContext& ctx) { + NGLOG_WARNING(Service_NFP, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(state)); + } + + void GetDeviceState(Kernel::HLERequestContext& ctx) { + NGLOG_WARNING(Service_NFP, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(device_state)); + } + + void GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 dev_handle = rp.Pop<u64>(); + NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(npad_id); + } + + void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 dev_handle = rp.Pop<u64>(); + NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(availability_change_event); + } + + const u64 device_handle{0xDEAD}; + const HID::ControllerID npad_id{HID::Controller_Player1}; + State state{State::NonInitialized}; + DeviceState device_state{DeviceState::Initialized}; + Kernel::SharedPtr<Kernel::Event> activate_event; + Kernel::SharedPtr<Kernel::Event> deactivate_event; + Kernel::SharedPtr<Kernel::Event> availability_change_event; }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index eee92cfcd..62489c7fe 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -38,7 +38,7 @@ public: {8, nullptr, "SetPriority"}, {9, nullptr, "SetNetworkProfileId"}, {10, nullptr, "SetRejectable"}, - {11, nullptr, "SetConnectionConfirmationOption"}, + {11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"}, {12, nullptr, "SetPersistent"}, {13, nullptr, "SetInstant"}, {14, nullptr, "SetSustainable"}, @@ -67,23 +67,32 @@ private: rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); } + void GetResult(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 2}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(event1, event2); } + void Cancel(Kernel::HLERequestContext& ctx) { NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) { + NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + Kernel::SharedPtr<Kernel::Event> event1, event2; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index a9538ff43..0abc0de83 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -26,6 +26,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec return ZCullGetInfo(input, output); case IoctlCommand::IocZbcSetTable: return ZBCSetTable(input, output); + case IoctlCommand::IocZbcQueryTable: + return ZBCQueryTable(input, output); + case IoctlCommand::IocFlushL2: + return FlushL2(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -136,4 +140,22 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& return 0; } +u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { + NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlZbcQueryTable params{}; + std::memcpy(¶ms, input.data(), input.size()); + // TODO : To implement properly + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { + NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlFlushL2 params{}; + std::memcpy(¶ms, input.data(), input.size()); + // TODO : To implement properly + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 1d5ba2e67..f09113e67 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -26,6 +26,18 @@ private: IocZcullGetCtxSizeCommand = 0x80044701, IocZcullGetInfo = 0x80284702, IocZbcSetTable = 0x402C4703, + IocZbcQueryTable = 0xC0344704, + IocFlushL2 = 0x40084707, + IocInvalICache = 0x4008470D, + IocSetMmudebugMode = 0x4008470E, + IocSetSmDebugMode = 0x4010470F, + IocWaitForPause = 0xC0084710, + IocGetTcpExceptionEnStatus = 0x80084711, + IocNumVsms = 0x80084712, + IocVsmsMapping = 0xC0044713, + IocGetErrorChannelUserData = 0xC008471B, + IocGetGpuTime = 0xC010471C, + IocGetCpuTimeCorrelationInfo = 0xC108471D, }; struct IoctlGpuCharacteristics { @@ -127,12 +139,31 @@ private: }; static_assert(sizeof(IoctlZbcSetTable) == 44, "IoctlZbcSetTable is incorrect size"); + struct IoctlZbcQueryTable { + u32_le color_ds[4]; + u32_le color_l2[4]; + u32_le depth; + u32_le ref_cnt; + u32_le format; + u32_le type; + u32_le index_size; + }; + static_assert(sizeof(IoctlZbcQueryTable) == 52, "IoctlZbcQueryTable is incorrect size"); + + struct IoctlFlushL2 { + u32_le flush; // l2_flush | l2_invalidate << 1 | fb_flush << 2 + u32_le reserved; + }; + static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); + u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); + u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index ef12d9300..86e9dc998 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -354,6 +354,40 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt return textures; } +Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const { + auto& shader = state.shader_stages[static_cast<size_t>(stage)]; + auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; + ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); + + GPUVAddr tex_info_address = tex_info_buffer.address + offset * sizeof(Texture::TextureHandle); + + ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); + + boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); + Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)}; + + Texture::FullTextureInfo tex_info{}; + tex_info.index = static_cast<u32>(offset); + + // Load the TIC data. + if (tex_handle.tic_id != 0) { + tex_info.enabled = true; + + auto tic_entry = GetTICEntry(tex_handle.tic_id); + // TODO(Subv): Workaround for BitField's move constructor being deleted. + std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry)); + } + + // Load the TSC data + if (tex_handle.tsc_id != 0) { + auto tsc_entry = GetTSCEntry(tex_handle.tsc_id); + // TODO(Subv): Workaround for BitField's move constructor being deleted. + std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry)); + } + + return tex_info; +} + u32 Maxwell3D::GetRegisterValue(u32 method) const { ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register"); return regs.reg_array[method]; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 245410c95..56b837372 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -664,6 +664,9 @@ public: /// Returns a list of enabled textures for the specified shader stage. std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; + /// Returns the texture information for a specific texture in a specific shader stage. + Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; + /// Returns whether the specified shader stage is enabled or not. bool IsShaderStageEnabled(Regs::ShaderStage stage) const; diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 8d4ea3401..32800392b 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -109,11 +109,6 @@ union Sampler { u64 value{}; }; -union Uniform { - BitField<20, 14, u64> offset; - BitField<34, 5, u64> index; -}; - } // namespace Shader } // namespace Tegra @@ -173,6 +168,31 @@ enum class SubOp : u64 { Min = 0x8, }; +enum class F2iRoundingOp : u64 { + None = 0, + Floor = 1, + Ceil = 2, + Trunc = 3, +}; + +enum class F2fRoundingOp : u64 { + None = 0, + Pass = 3, + Round = 8, + Floor = 9, + Ceil = 10, + Trunc = 11, +}; + +enum class UniformType : u64 { + UnsignedByte = 0, + SignedByte = 1, + UnsignedShort = 2, + SignedShort = 3, + Single = 4, + Double = 5, +}; + union Instruction { Instruction& operator=(const Instruction& instr) { value = instr.value; @@ -230,23 +250,31 @@ union Instruction { std::memcpy(&result, &imm, sizeof(imm)); return result; } + + s32 GetSignedImm20_20() const { + u32 immediate = static_cast<u32>(imm20_19 | (negate_imm << 19)); + // Sign extend the 20-bit value. + u32 mask = 1U << (20 - 1); + return static_cast<s32>((immediate ^ mask) - mask); + } } alu; union { BitField<39, 5, u64> shift_amount; - BitField<20, 19, u64> immediate_low; - BitField<56, 1, u64> immediate_high; BitField<48, 1, u64> negate_b; BitField<49, 1, u64> negate_a; + } iscadd; - s32 GetImmediate() const { - u32 immediate = static_cast<u32>(immediate_low | (immediate_high << 19)); - // Sign extend the 20-bit value. - u32 mask = 1U << (20 - 1); - return static_cast<s32>((immediate ^ mask) - mask); - } + union { + BitField<20, 8, u64> shift_position; + BitField<28, 8, u64> shift_length; + BitField<48, 1, u64> negate_b; + BitField<49, 1, u64> negate_a; - } iscadd; + u64 GetLeftShiftValue() const { + return 32 - (shift_position + shift_length); + } + } bfe; union { BitField<48, 1, u64> negate_b; @@ -254,6 +282,11 @@ union Instruction { } ffma; union { + BitField<48, 3, UniformType> type; + BitField<44, 2, u64> unknown; + } ld_c; + + union { BitField<0, 3, u64> pred0; BitField<3, 3, u64> pred3; BitField<7, 1, u64> abs_a; @@ -293,11 +326,20 @@ union Instruction { union { BitField<10, 2, Register::Size> size; - BitField<13, 1, u64> is_signed; + BitField<12, 1, u64> is_output_signed; + BitField<13, 1, u64> is_input_signed; BitField<41, 2, u64> selector; BitField<45, 1, u64> negate_a; BitField<49, 1, u64> abs_a; BitField<50, 1, u64> saturate_a; + + union { + BitField<39, 2, F2iRoundingOp> rounding; + } f2i; + + union { + BitField<39, 4, F2fRoundingOp> rounding; + } f2f; } conversion; union { @@ -328,24 +370,34 @@ union Instruction { } texs; union { - BitField<20, 5, u64> target; + BitField<20, 24, u64> target; BitField<5, 1, u64> constant_buffer; s32 GetBranchTarget() const { // Sign extend the branch target offset - u32 mask = 1U << (5 - 1); + u32 mask = 1U << (24 - 1); u32 value = static_cast<u32>(target); - // The branch offset is relative to the next instruction, so add 1 to it. - return static_cast<s32>((value ^ mask) - mask) + 1; + // The branch offset is relative to the next instruction and is stored in bytes, so + // divide it by the size of an instruction and add 1 to it. + return static_cast<s32>((value ^ mask) - mask) / sizeof(Instruction) + 1; } } bra; + union { + BitField<20, 14, u64> offset; + BitField<34, 5, u64> index; + } cbuf34; + + union { + BitField<20, 16, s64> offset; + BitField<36, 5, u64> index; + } cbuf36; + BitField<61, 1, u64> is_b_imm; BitField<60, 1, u64> is_b_gpr; BitField<59, 1, u64> is_c_gpr; Attribute attribute; - Uniform uniform; Sampler sampler; u64 value; @@ -358,8 +410,12 @@ class OpCode { public: enum class Id { KIL, + BFE_C, + BFE_R, + BFE_IMM, BRA, LD_A, + LD_C, ST_A, TEX, TEXQ, // Texture Query @@ -402,12 +458,18 @@ public: MOV_R, MOV_IMM, MOV32_IMM, + SHL_C, + SHL_R, + SHL_IMM, SHR_C, SHR_R, SHR_IMM, FMNMX_C, FMNMX_R, FMNMX_IMM, + IMNMX_C, + IMNMX_R, + IMNMX_IMM, FSETP_C, // Set Predicate FSETP_R, FSETP_IMM, @@ -418,12 +480,18 @@ public: ISETP_IMM, ISETP_R, PSETP, + XMAD_IMM, + XMAD_CR, + XMAD_RC, + XMAD_RR, }; enum class Type { Trivial, Arithmetic, + Bfe, Logic, + Shift, ScaledAdd, Ffma, Flow, @@ -530,6 +598,7 @@ private: INST("111000110011----", Id::KIL, Type::Flow, "KIL"), INST("111000100100----", Id::BRA, Type::Flow, "BRA"), INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), + INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), INST("1100000000111---", Id::TEX, Type::Memory, "TEX"), INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), @@ -558,20 +627,29 @@ private: INST("0100110010101---", Id::F2F_C, Type::Conversion, "F2F_C"), INST("0101110010101---", Id::F2F_R, Type::Conversion, "F2F_R"), INST("0011100-10101---", Id::F2F_IMM, Type::Conversion, "F2F_IMM"), - INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"), - INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"), - INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"), + INST("0100110010110---", Id::F2I_C, Type::Conversion, "F2I_C"), + INST("0101110010110---", Id::F2I_R, Type::Conversion, "F2I_R"), + INST("0011100-10110---", Id::F2I_IMM, Type::Conversion, "F2I_IMM"), INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"), - INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"), - INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"), - INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"), INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), + INST("0100110000100---", Id::IMNMX_C, Type::Arithmetic, "FMNMX_IMM"), + INST("0101110000100---", Id::IMNMX_R, Type::Arithmetic, "FMNMX_IMM"), + INST("0011100-00100---", Id::IMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), + INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), + INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), + INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), INST("000001----------", Id::LOP32I, Type::Logic, "LOP32I"), + INST("0100110001001---", Id::SHL_C, Type::Shift, "SHL_C"), + INST("0101110001001---", Id::SHL_R, Type::Shift, "SHL_R"), + INST("0011100-01001---", Id::SHL_IMM, Type::Shift, "SHL_IMM"), + INST("0100110000101---", Id::SHR_C, Type::Shift, "SHR_C"), + INST("0101110000101---", Id::SHR_R, Type::Shift, "SHR_R"), + INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"), INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"), INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"), INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"), @@ -588,6 +666,10 @@ private: INST("010110110110----", Id::ISETP_R, Type::IntegerSetPredicate, "ISETP_R"), INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerSetPredicate, "ISETP_IMM"), INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), + INST("0011011-00------", Id::XMAD_IMM, Type::Arithmetic, "XMAD_IMM"), + INST("0100111---------", Id::XMAD_CR, Type::Arithmetic, "XMAD_CR"), + INST("010100010-------", Id::XMAD_RC, Type::Arithmetic, "XMAD_RC"), + INST("0101101100------", Id::XMAD_RR, Type::Arithmetic, "XMAD_RR"), }; #undef INST std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 756518ee7..66351fe6e 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -26,6 +26,10 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { ASSERT(format != RenderTargetFormat::NONE); switch (format) { + case RenderTargetFormat::RGBA32_FLOAT: + return 16; + case RenderTargetFormat::RGBA16_FLOAT: + return 8; case RenderTargetFormat::RGBA8_UNORM: case RenderTargetFormat::RGB10_A2_UNORM: return 4; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index f168a5171..5852b9619 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -15,10 +15,12 @@ namespace Tegra { enum class RenderTargetFormat : u32 { NONE = 0x0, + RGBA32_FLOAT = 0xC0, RGBA16_FLOAT = 0xCA, RGB10_A2_UNORM = 0xD1, RGBA8_UNORM = 0xD5, RGBA8_SRGB = 0xD6, + R11G11B10_FLOAT = 0xE0, }; /// Returns the number of bytes per pixel of each rendertarget format. diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 0a33868b7..2e90ebcf4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -196,8 +196,10 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!"); - // Next available bindpoint to use when uploading the const buffers to the GLSL shaders. + // Next available bindpoints to use when uploading the const buffers and textures to the GLSL + // shaders. u32 current_constbuffer_bindpoint = 0; + u32 current_texture_bindpoint = 0; for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { auto& shader_config = gpu.regs.shader_config[index]; @@ -258,6 +260,11 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, current_constbuffer_bindpoint, shader_resources.const_buffer_entries); + + // Configure the textures for this shader stage. + current_texture_bindpoint = + SetupTextures(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, + current_texture_bindpoint, shader_resources.texture_samplers); } shader_program_manager->UseTrivialGeometryShader(); @@ -341,9 +348,6 @@ void RasterizerOpenGL::DrawArrays() { // TODO(bunnei): Sync framebuffer_scale uniform here // TODO(bunnei): Sync scissorbox uniform(s) here - // Sync and bind the texture surfaces - BindTextures(); - // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable // scissor test to prevent drawing outside of the framebuffer region state.scissor.enabled = true; @@ -447,39 +451,6 @@ void RasterizerOpenGL::DrawArrays() { } } -void RasterizerOpenGL::BindTextures() { - using Regs = Tegra::Engines::Maxwell3D::Regs; - auto& maxwell3d = Core::System::GetInstance().GPU().Get3DEngine(); - - // Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a - // certain number in OpenGL. We try to only use the minimum amount of host textures by not - // keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8 - // can be host texture id 0 if it's the only texture used in the guest shader program. - u32 host_texture_index = 0; - for (u32 stage = 0; stage < Regs::MaxShaderStage; ++stage) { - ASSERT(host_texture_index < texture_samplers.size()); - const auto textures = maxwell3d.GetStageTextures(static_cast<Regs::ShaderStage>(stage)); - for (unsigned texture_index = 0; texture_index < textures.size(); ++texture_index) { - const auto& texture = textures[texture_index]; - - if (texture.enabled) { - texture_samplers[host_texture_index].SyncWithConfig(texture.tsc); - Surface surface = res_cache.GetTextureSurface(texture); - if (surface != nullptr) { - state.texture_units[host_texture_index].texture_2d = surface->texture.handle; - } else { - // Can occur when texture addr is null or its memory is unmapped/invalid - state.texture_units[texture_index].texture_2d = 0; - } - - ++host_texture_index; - } else { - state.texture_units[texture_index].texture_2d = 0; - } - } - } -} - void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; switch (method) { @@ -654,7 +625,16 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr buffer_draw_state.bindpoint = current_bindpoint + bindpoint; boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); - std::vector<u8> data(used_buffer.GetSize() * sizeof(float)); + + std::vector<u8> data; + if (used_buffer.IsIndirect()) { + // Buffer is accessed indirectly, so upload the entire thing + data.resize(buffer.size * sizeof(float)); + } else { + // Buffer is accessed directly, upload just what we use + data.resize(used_buffer.GetSize() * sizeof(float)); + } + Memory::ReadBlock(*addr, data.data(), data.size()); glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo); @@ -674,6 +654,52 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr return current_bindpoint + entries.size(); } +u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, + const std::vector<GLShader::SamplerEntry>& entries) { + auto& gpu = Core::System::GetInstance().GPU(); + auto& maxwell3d = gpu.Get3DEngine(); + + ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage), + "Attempted to upload textures of disabled shader stage"); + + ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units), + "Exceeded the number of active textures."); + + for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { + const auto& entry = entries[bindpoint]; + u32 current_bindpoint = current_unit + bindpoint; + + // Bind the uniform to the sampler. + GLint uniform = glGetUniformLocation(program, entry.GetName().c_str()); + ASSERT(uniform != -1); + glProgramUniform1i(program, uniform, current_bindpoint); + + const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); + ASSERT(texture.enabled); + + texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); + Surface surface = res_cache.GetTextureSurface(texture); + if (surface != nullptr) { + state.texture_units[current_bindpoint].texture_2d = surface->texture.handle; + state.texture_units[current_bindpoint].swizzle.r = + MaxwellToGL::SwizzleSource(texture.tic.x_source); + state.texture_units[current_bindpoint].swizzle.g = + MaxwellToGL::SwizzleSource(texture.tic.y_source); + state.texture_units[current_bindpoint].swizzle.b = + MaxwellToGL::SwizzleSource(texture.tic.z_source); + state.texture_units[current_bindpoint].swizzle.a = + MaxwellToGL::SwizzleSource(texture.tic.w_source); + } else { + // Can occur when texture addr is null or its memory is unmapped/invalid + state.texture_units[current_bindpoint].texture_2d = 0; + } + } + + state.Apply(); + + return current_unit + entries.size(); +} + void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, bool has_stencil) { state.draw.draw_framebuffer = framebuffer.handle; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 4b915c76a..d3f0558ed 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -80,9 +80,6 @@ private: void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, bool has_stencil); - /// Binds the required textures to OpenGL before drawing a batch. - void BindTextures(); - /* * Configures the current constbuffers to use for the draw command. * @param stage The shader stage to configure buffers for. @@ -95,6 +92,17 @@ private: u32 current_bindpoint, const std::vector<GLShader::ConstBufferEntry>& entries); + /* + * Configures the current textures to use for the draw command. + * @param stage The shader stage to configure textures for. + * @param program The OpenGL program object that contains the specified stage. + * @param current_unit The offset at which to start counting unused texture units. + * @param entries Vector describing the textures that are actually used in the guest shader. + * @returns The next available bindpoint for use in the next shader stage. + */ + u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program, + u32 current_unit, const std::vector<GLShader::SamplerEntry>& entries); + /// Syncs the viewport to match the guest state void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index d6048f639..df2474ea2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -50,6 +50,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5 {GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F + {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45 @@ -60,8 +61,10 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); if (type == SurfaceType::ColorTexture) { ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); - // For now only UNORM components are supported, or RGBA16F which is type FLOAT - ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F); + // For now only UNORM components are supported, or either R11FG11FB10F or RGBA16F which are + // type FLOAT + ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F || + pixel_format == PixelFormat::R11FG11FB10F); return tex_format_tuples[static_cast<unsigned int>(pixel_format)]; } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { // TODO(Subv): Implement depth formats @@ -110,11 +113,12 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra: Tegra::GPUVAddr), SurfaceParams::MaxPixelFormat> morton_to_gl_fns = { - MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, - MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>, - MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>, - MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, - MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, + MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, + MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>, + MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>, + MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::DXT1>, + MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>, + MortonCopy<true, PixelFormat::DXN1>, }; static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr, @@ -127,6 +131,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra: MortonCopy<false, PixelFormat::A1B5G5R5>, MortonCopy<false, PixelFormat::R8>, MortonCopy<false, PixelFormat::RGBA16F>, + MortonCopy<false, PixelFormat::R11FG11FB10F>, // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported nullptr, nullptr, @@ -164,60 +169,10 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, GLuint read_fb_handle, GLuint draw_fb_handle) { - OpenGLState state = OpenGLState::GetCurState(); - - OpenGLState prev_state = state; - SCOPE_EXIT({ prev_state.Apply(); }); - - // Make sure textures aren't bound to texture units, since going to bind them to framebuffer - // components - state.ResetTexture(src_tex); - state.ResetTexture(dst_tex); - - state.draw.read_framebuffer = read_fb_handle; - state.draw.draw_framebuffer = draw_fb_handle; - state.Apply(); - - u32 buffers = 0; - - if (type == SurfaceType::ColorTexture) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex, - 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, - 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - buffers = GL_COLOR_BUFFER_BIT; - } else if (type == SurfaceType::Depth) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - - buffers = GL_DEPTH_BUFFER_BIT; - } else if (type == SurfaceType::DepthStencil) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - src_tex, 0); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - dst_tex, 0); - - buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; - } - - glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left, - dst_rect.bottom, dst_rect.right, dst_rect.top, buffers, - buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); + glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, dst_tex, + GL_TEXTURE_2D, 0, dst_rect.left, dst_rect.bottom, 0, src_rect.GetWidth(), + src_rect.GetHeight(), 0); return true; } @@ -1097,16 +1052,19 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu params.UpdateParams(); - if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0 || + if (params.GetActualWidth() % 8 != 0 || params.GetActualHeight() % 8 != 0 || params.stride != params.width) { Surface src_surface; MathUtil::Rectangle<u32> rect; std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); + rect = rect.Scale(params.GetCompresssionFactor()); + params.res_scale = src_surface->res_scale; Surface tmp_surface = CreateSurface(params); - BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, - tmp_surface->GetScaledRect(), + + auto dst_rect = tmp_surface->GetScaledRect().Scale(params.GetCompresssionFactor()); + BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, dst_rect, SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle, draw_framebuffer.handle); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 6f08678ab..0f43e863d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -60,10 +60,11 @@ struct SurfaceParams { A1B5G5R5 = 3, R8 = 4, RGBA16F = 5, - DXT1 = 6, - DXT23 = 7, - DXT45 = 8, - DXN1 = 9, // This is also known as BC4 + R11FG11FB10F = 6, + DXT1 = 7, + DXT23 = 8, + DXT45 = 9, + DXN1 = 10, // This is also known as BC4 Max, Invalid = 255, @@ -104,7 +105,8 @@ struct SurfaceParams { 1, // A2B10G10R10 1, // A1B5G5R5 1, // R8 - 2, // RGBA16F + 1, // RGBA16F + 1, // R11FG11FB10F 4, // DXT1 4, // DXT23 4, // DXT45 @@ -129,6 +131,7 @@ struct SurfaceParams { 16, // A1B5G5R5 8, // R8 64, // RGBA16F + 32, // R11FG11FB10F 64, // DXT1 128, // DXT23 128, // DXT45 @@ -151,6 +154,8 @@ struct SurfaceParams { return PixelFormat::A2B10G10R10; case Tegra::RenderTargetFormat::RGBA16_FLOAT: return PixelFormat::RGBA16F; + case Tegra::RenderTargetFormat::R11G11B10_FLOAT: + return PixelFormat::R11FG11FB10F; default: NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); @@ -182,6 +187,8 @@ struct SurfaceParams { return PixelFormat::R8; case Tegra::Texture::TextureFormat::R16_G16_B16_A16: return PixelFormat::RGBA16F; + case Tegra::Texture::TextureFormat::BF10GF11RF11: + return PixelFormat::R11FG11FB10F; case Tegra::Texture::TextureFormat::DXT1: return PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: @@ -211,6 +218,8 @@ struct SurfaceParams { return Tegra::Texture::TextureFormat::R8; case PixelFormat::RGBA16F: return Tegra::Texture::TextureFormat::R16_G16_B16_A16; + case PixelFormat::R11FG11FB10F: + return Tegra::Texture::TextureFormat::BF10GF11RF11; case PixelFormat::DXT1: return Tegra::Texture::TextureFormat::DXT1; case PixelFormat::DXT23: @@ -243,6 +252,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RGB10_A2_UNORM: return ComponentType::UNorm; case Tegra::RenderTargetFormat::RGBA16_FLOAT: + case Tegra::RenderTargetFormat::R11G11B10_FLOAT: return ComponentType::Float; default: NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index eb8d37c9b..94c6bc4b2 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -20,7 +20,6 @@ using Tegra::Shader::OpCode; using Tegra::Shader::Register; using Tegra::Shader::Sampler; using Tegra::Shader::SubOp; -using Tegra::Shader::Uniform; constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; @@ -365,11 +364,9 @@ public: } /// Generates code representing a uniform (C buffer) register, interpreted as the input type. - std::string GetUniform(const Uniform& uniform, GLSLRegister::Type type) { - declr_const_buffers[uniform.index].MarkAsUsed(static_cast<unsigned>(uniform.index), - static_cast<unsigned>(uniform.offset), stage); - std::string value = - 'c' + std::to_string(uniform.index) + '[' + std::to_string(uniform.offset) + ']'; + std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type) { + declr_const_buffers[index].MarkAsUsed(index, offset, stage); + std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset) + ']'; if (type == GLSLRegister::Type::Float) { return value; @@ -380,10 +377,19 @@ public: } } - /// Generates code representing a uniform (C buffer) register, interpreted as the type of the - /// destination register. - std::string GetUniform(const Uniform& uniform, const Register& dest_reg) { - return GetUniform(uniform, regs[dest_reg].GetActiveType()); + std::string GetUniformIndirect(u64 index, s64 offset, const Register& index_reg, + GLSLRegister::Type type) { + declr_const_buffers[index].MarkAsUsedIndirect(index, stage); + std::string value = 'c' + std::to_string(index) + "[(floatBitsToInt(" + + GetRegister(index_reg, 0) + ") + " + std::to_string(offset) + ") / 4]"; + + if (type == GLSLRegister::Type::Float) { + return value; + } else if (type == GLSLRegister::Type::Integer) { + return "floatBitsToInt(" + value + ')'; + } else { + UNREACHABLE(); + } } /// Add declarations for registers @@ -425,6 +431,14 @@ public: ++const_buffer_layout; } declarations.AddNewLine(); + + // Append the sampler2D array for the used textures. + size_t num_samplers = GetSamplers().size(); + if (num_samplers > 0) { + declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + + std::to_string(num_samplers) + "];"); + declarations.AddNewLine(); + } } /// Returns a list of constant buffer declarations @@ -435,6 +449,32 @@ public: return result; } + /// Returns a list of samplers used in the shader + std::vector<SamplerEntry> GetSamplers() const { + return used_samplers; + } + + /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if + /// necessary. + std::string AccessSampler(const Sampler& sampler) { + size_t offset = static_cast<size_t>(sampler.index.Value()); + + // If this sampler has already been used, return the existing mapping. + auto itr = + std::find_if(used_samplers.begin(), used_samplers.end(), + [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); + + if (itr != used_samplers.end()) { + return itr->GetName(); + } + + // Otherwise create a new mapping for this sampler + size_t next_index = used_samplers.size(); + SamplerEntry entry{stage, offset, next_index}; + used_samplers.emplace_back(entry); + return entry.GetName(); + } + private: /// Build GLSL conversion function, e.g. floatBitsToInt, intBitsToFloat, etc. const std::string GetGLSLConversionFunc(GLSLRegister::Type src, GLSLRegister::Type dest) const { @@ -544,6 +584,7 @@ private: std::set<Attribute::Index> declr_input_attribute; std::set<Attribute::Index> declr_output_attribute; std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; + std::vector<SamplerEntry> used_samplers; const Maxwell3D::Regs::ShaderStage& stage; }; @@ -563,7 +604,7 @@ public: /// Returns entries in the shader that are useful for external functions ShaderEntries GetEntries() const { - return {regs.GetConstBuffersDeclarations()}; + return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; } private: @@ -585,12 +626,8 @@ private: } /// Generates code representing a texture sampler. - std::string GetSampler(const Sampler& sampler) const { - // TODO(Subv): Support more than just texture sampler 0 - ASSERT_MSG(sampler.index == Sampler::Index::Sampler_0, "unsupported"); - const unsigned index{static_cast<unsigned>(sampler.index.Value()) - - static_cast<unsigned>(Sampler::Index::Sampler_0)}; - return "tex[" + std::to_string(index) + ']'; + std::string GetSampler(const Sampler& sampler) { + return regs.AccessSampler(sampler); } /** @@ -747,7 +784,8 @@ private: if (instr.is_b_gpr) { op_b += regs.GetRegisterAsFloat(instr.gpr20); } else { - op_b += regs.GetUniform(instr.uniform, instr.gpr0); + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Float); } } @@ -850,8 +888,33 @@ private: } break; } + case OpCode::Type::Bfe: { + ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); + + std::string op_a = instr.bfe.negate_a ? "-" : ""; + op_a += regs.GetRegisterAsInteger(instr.gpr8); + + switch (opcode->GetId()) { + case OpCode::Id::BFE_IMM: { + std::string inner_shift = + '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; + std::string outer_shift = + '(' + inner_shift + " >> " + + std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; + + regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); + break; + } + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + + break; + } case OpCode::Type::Logic: { - std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, false); + std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); if (instr.alu.lop.invert_a) op_a = "~(" + op_a + ')'; @@ -865,17 +928,17 @@ private: switch (instr.alu.lop.operation) { case Tegra::Shader::LogicOperation::And: { - regs.SetRegisterToInteger(instr.gpr0, false, 0, + regs.SetRegisterToInteger(instr.gpr0, true, 0, '(' + op_a + " & " + std::to_string(imm) + ')', 1, 1); break; } case Tegra::Shader::LogicOperation::Or: { - regs.SetRegisterToInteger(instr.gpr0, false, 0, + regs.SetRegisterToInteger(instr.gpr0, true, 0, '(' + op_a + " | " + std::to_string(imm) + ')', 1, 1); break; } case Tegra::Shader::LogicOperation::Xor: { - regs.SetRegisterToInteger(instr.gpr0, false, 0, + regs.SetRegisterToInteger(instr.gpr0, true, 0, '(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1); break; } @@ -893,6 +956,36 @@ private: } break; } + + case OpCode::Type::Shift: { + std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); + std::string op_b; + + if (instr.is_b_imm) { + op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')'; + } else { + if (instr.is_b_gpr) { + op_b += regs.GetRegisterAsInteger(instr.gpr20); + } else { + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Integer); + } + } + + switch (opcode->GetId()) { + case OpCode::Id::SHL_C: + case OpCode::Id::SHL_R: + case OpCode::Id::SHL_IMM: + regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); + break; + default: { + NGLOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + break; + } + case OpCode::Type::ScaledAdd: { std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); @@ -902,12 +995,13 @@ private: std::string op_b = instr.iscadd.negate_b ? "-" : ""; if (instr.is_b_imm) { - op_b += '(' + std::to_string(instr.iscadd.GetImmediate()) + ')'; + op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')'; } else { if (instr.is_b_gpr) { op_b += regs.GetRegisterAsInteger(instr.gpr20); } else { - op_b += regs.GetUniform(instr.uniform, instr.gpr0); + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Integer); } } @@ -924,7 +1018,8 @@ private: switch (opcode->GetId()) { case OpCode::Id::FFMA_CR: { - op_b += regs.GetUniform(instr.uniform, instr.gpr0); + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Float); op_c += regs.GetRegisterAsFloat(instr.gpr39); break; } @@ -935,7 +1030,8 @@ private: } case OpCode::Id::FFMA_RC: { op_b += regs.GetRegisterAsFloat(instr.gpr39); - op_c += regs.GetUniform(instr.uniform, instr.gpr0); + op_c += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Float); break; } case OpCode::Id::FFMA_IMM: { @@ -962,18 +1058,20 @@ private: ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); std::string op_a = - regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed); + regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; } - regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, + 1); break; } case OpCode::Id::I2F_R: { + ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); std::string op_a = - regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed); + regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; @@ -985,6 +1083,25 @@ private: case OpCode::Id::F2F_R: { std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); + switch (instr.conversion.f2f.rounding) { + case Tegra::Shader::F2fRoundingOp::None: + break; + case Tegra::Shader::F2fRoundingOp::Floor: + op_a = "floor(" + op_a + ')'; + break; + case Tegra::Shader::F2fRoundingOp::Ceil: + op_a = "ceil(" + op_a + ')'; + break; + case Tegra::Shader::F2fRoundingOp::Trunc: + op_a = "trunc(" + op_a + ')'; + break; + default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", + static_cast<u32>(instr.conversion.f2f.rounding.Value())); + UNREACHABLE(); + break; + } + if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; } @@ -992,6 +1109,42 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); break; } + case OpCode::Id::F2I_R: { + std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); + + if (instr.conversion.abs_a) { + op_a = "abs(" + op_a + ')'; + } + + switch (instr.conversion.f2i.rounding) { + case Tegra::Shader::F2iRoundingOp::None: + break; + case Tegra::Shader::F2iRoundingOp::Floor: + op_a = "floor(" + op_a + ')'; + break; + case Tegra::Shader::F2iRoundingOp::Ceil: + op_a = "ceil(" + op_a + ')'; + break; + case Tegra::Shader::F2iRoundingOp::Trunc: + op_a = "trunc(" + op_a + ')'; + break; + default: + NGLOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", + static_cast<u32>(instr.conversion.f2i.rounding.Value())); + UNREACHABLE(); + break; + } + + if (instr.conversion.is_output_signed) { + op_a = "int(" + op_a + ')'; + } else { + op_a = "uint(" + op_a + ')'; + } + + regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, + 1); + break; + } default: { NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); UNREACHABLE(); @@ -1000,23 +1153,47 @@ private: break; } case OpCode::Type::Memory: { - const Attribute::Index attribute = instr.attribute.fmt20.index; - switch (opcode->GetId()) { case OpCode::Id::LD_A: { ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); regs.SetRegisterToInputAttibute(instr.gpr0, instr.attribute.fmt20.element, - attribute); + instr.attribute.fmt20.index); + break; + } + case OpCode::Id::LD_C: { + ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); + + std::string op_a = + regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, instr.gpr8, + GLSLRegister::Type::Float); + std::string op_b = + regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, instr.gpr8, + GLSLRegister::Type::Float); + + switch (instr.ld_c.type.Value()) { + case Tegra::Shader::UniformType::Single: + regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + break; + + case Tegra::Shader::UniformType::Double: + regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + regs.SetRegisterToFloat(instr.gpr0.Value() + 1, 0, op_b, 1, 1); + break; + + default: + NGLOG_CRITICAL(HW_GPU, "Unhandled type: {}", + static_cast<unsigned>(instr.ld_c.type.Value())); + UNREACHABLE(); + } break; } case OpCode::Id::ST_A: { ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); - regs.SetOutputAttributeToRegister(attribute, instr.attribute.fmt20.element, - instr.gpr0); + regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index, + instr.attribute.fmt20.element, instr.gpr0); break; } case OpCode::Id::TEX: { - ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested"); const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const std::string sampler = GetSampler(instr.sampler); @@ -1029,7 +1206,7 @@ private: const std::string texture = "texture(" + sampler + ", coords)"; size_t dest_elem{}; - for (size_t elem = 0; elem < instr.attribute.fmt20.size; ++elem) { + for (size_t elem = 0; elem < 4; ++elem) { if (!instr.tex.IsComponentEnabled(elem)) { // Skip disabled components continue; @@ -1042,7 +1219,6 @@ private: break; } case OpCode::Id::TEXS: { - ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested"); const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); const std::string sampler = GetSampler(instr.sampler); @@ -1105,7 +1281,8 @@ private: if (instr.is_b_gpr) { op_b += regs.GetRegisterAsFloat(instr.gpr20); } else { - op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Float); + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Float); } } @@ -1138,15 +1315,17 @@ private: } case OpCode::Type::IntegerSetPredicate: { std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed); + std::string op_b; - std::string op_b{}; - - ASSERT_MSG(!instr.is_b_imm, "ISETP_IMM not implemented"); - - if (instr.is_b_gpr) { - op_b += regs.GetRegisterAsInteger(instr.gpr20, 0, instr.isetp.is_signed); + if (instr.is_b_imm) { + op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')'; } else { - op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Integer); + if (instr.is_b_gpr) { + op_b += regs.GetRegisterAsInteger(instr.gpr20, 0, instr.isetp.is_signed); + } else { + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Integer); + } } using Tegra::Shader::Pred; @@ -1192,7 +1371,8 @@ private: if (instr.is_b_gpr) { op_b += regs.GetRegisterAsFloat(instr.gpr20); } else { - op_b += regs.GetUniform(instr.uniform, GLSLRegister::Type::Float); + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Float); } } diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 254f6e2c3..b88d592b7 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -62,8 +62,6 @@ layout (std140) uniform fs_config { vec4 viewport_flip; }; -uniform sampler2D tex[32]; - void main() { exec_shader(); } diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 458032b5c..ed890e0f9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -22,17 +22,28 @@ class ConstBufferEntry { using Maxwell = Tegra::Engines::Maxwell3D::Regs; public: - void MarkAsUsed(unsigned index, unsigned offset, Maxwell::ShaderStage stage) { + void MarkAsUsed(u64 index, u64 offset, Maxwell::ShaderStage stage) { is_used = true; - this->index = index; + this->index = static_cast<unsigned>(index); + this->stage = stage; + max_offset = std::max(max_offset, static_cast<unsigned>(offset)); + } + + void MarkAsUsedIndirect(u64 index, Maxwell::ShaderStage stage) { + is_used = true; + is_indirect = true; + this->index = static_cast<unsigned>(index); this->stage = stage; - max_offset = std::max(max_offset, offset); } bool IsUsed() const { return is_used; } + bool IsIndirect() const { + return is_indirect; + } + unsigned GetIndex() const { return index; } @@ -51,13 +62,54 @@ private: }; bool is_used{}; + bool is_indirect{}; unsigned index{}; unsigned max_offset{}; Maxwell::ShaderStage stage; }; +class SamplerEntry { + using Maxwell = Tegra::Engines::Maxwell3D::Regs; + +public: + SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) + : offset(offset), stage(stage), sampler_index(index) {} + + size_t GetOffset() const { + return offset; + } + + size_t GetIndex() const { + return sampler_index; + } + + Maxwell::ShaderStage GetStage() const { + return stage; + } + + std::string GetName() const { + return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + + std::to_string(sampler_index) + ']'; + } + + static std::string GetArrayName(Maxwell::ShaderStage stage) { + return TextureSamplerNames[static_cast<size_t>(stage)]; + } + +private: + static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { + "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", + }; + /// Offset in TSC memory from which to read the sampler object, as specified by the sampling + /// instruction. + size_t offset; + Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. + size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. +}; + struct ShaderEntries { std::vector<ConstBufferEntry> const_buffer_entries; + std::vector<SamplerEntry> texture_samplers; }; using ProgramResult = std::pair<std::string, ShaderEntries>; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index ccdfc2718..7c00beb33 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -32,25 +32,6 @@ void SetShaderUniformBlockBindings(GLuint shader) { sizeof(MaxwellUniformData)); } -void SetShaderSamplerBindings(GLuint shader) { - OpenGLState cur_state = OpenGLState::GetCurState(); - GLuint old_program = std::exchange(cur_state.draw.shader_program, shader); - cur_state.Apply(); - - // Set the texture samplers to correspond to different texture units - for (u32 texture = 0; texture < NumTextureSamplers; ++texture) { - // Set the texture samplers to correspond to different texture units - std::string uniform_name = "tex[" + std::to_string(texture) + "]"; - GLint uniform_tex = glGetUniformLocation(shader, uniform_name.c_str()); - if (uniform_tex != -1) { - glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id); - } - } - - cur_state.draw.shader_program = old_program; - cur_state.Apply(); -} - } // namespace Impl void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index e963b4b7e..4295c20a6 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -45,7 +45,6 @@ public: shader.Create(program_result.first.c_str(), type); program.Create(true, shader.handle); Impl::SetShaderUniformBlockBindings(program.handle); - Impl::SetShaderSamplerBindings(program.handle); entries = program_result.second; } GLuint GetHandle() const { diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index f91dfe36a..44f0c8a01 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -50,6 +50,10 @@ OpenGLState::OpenGLState() { for (auto& texture_unit : texture_units) { texture_unit.texture_2d = 0; texture_unit.sampler = 0; + texture_unit.swizzle.r = GL_RED; + texture_unit.swizzle.g = GL_GREEN; + texture_unit.swizzle.b = GL_BLUE; + texture_unit.swizzle.a = GL_ALPHA; } lighting_lut.texture_buffer = 0; @@ -200,6 +204,15 @@ void OpenGLState::Apply() const { if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { glBindSampler(i, texture_units[i].sampler); } + // Update the texture swizzle + if (texture_units[i].swizzle.r != cur_state.texture_units[i].swizzle.r || + texture_units[i].swizzle.g != cur_state.texture_units[i].swizzle.g || + texture_units[i].swizzle.b != cur_state.texture_units[i].swizzle.b || + texture_units[i].swizzle.a != cur_state.texture_units[i].swizzle.a) { + std::array<GLint, 4> mask = {texture_units[i].swizzle.r, texture_units[i].swizzle.g, + texture_units[i].swizzle.b, texture_units[i].swizzle.a}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); + } } // Constbuffers diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 75c08e645..839e50e93 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -85,6 +85,12 @@ public: struct { GLuint texture_2d; // GL_TEXTURE_BINDING_2D GLuint sampler; // GL_SAMPLER_BINDING + struct { + GLint r; // GL_TEXTURE_SWIZZLE_R + GLint g; // GL_TEXTURE_SWIZZLE_G + GLint b; // GL_TEXTURE_SWIZZLE_B + GLint a; // GL_TEXTURE_SWIZZLE_A + } swizzle; } texture_units[32]; struct { diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index a630610d8..2155fb019 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -100,6 +100,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { switch (wrap_mode) { case Tegra::Texture::WrapMode::Wrap: return GL_REPEAT; + case Tegra::Texture::WrapMode::Mirror: + return GL_MIRRORED_REPEAT; case Tegra::Texture::WrapMode::ClampToEdge: return GL_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::ClampOGL: @@ -178,4 +180,25 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { return {}; } +inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { + switch (source) { + case Tegra::Texture::SwizzleSource::Zero: + return GL_ZERO; + case Tegra::Texture::SwizzleSource::R: + return GL_RED; + case Tegra::Texture::SwizzleSource::G: + return GL_GREEN; + case Tegra::Texture::SwizzleSource::B: + return GL_BLUE; + case Tegra::Texture::SwizzleSource::A: + return GL_ALPHA; + case Tegra::Texture::SwizzleSource::OneInt: + case Tegra::Texture::SwizzleSource::OneFloat: + return GL_ONE; + } + NGLOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); + UNREACHABLE(); + return {}; +} + } // namespace MaxwellToGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 3440d2190..f33766bfd 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -316,6 +316,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, }}; state.texture_units[0].texture_2d = screen_info.display_texture; + state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; state.Apply(); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 2d2af5554..7bf9c4c4b 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -55,6 +55,7 @@ u32 BytesPerPixel(TextureFormat format) { return 16; case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: + case TextureFormat::BF10GF11RF11: return 4; case TextureFormat::A1B5G5R5: case TextureFormat::B5G6R5: @@ -92,6 +93,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, case TextureFormat::B5G6R5: case TextureFormat::R8: case TextureFormat::R16_G16_B16_A16: + case TextureFormat::BF10GF11RF11: CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, unswizzled_data.data(), true, block_height); break; @@ -118,6 +120,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat case TextureFormat::A1B5G5R5: case TextureFormat::B5G6R5: case TextureFormat::R8: + case TextureFormat::BF10GF11RF11: // TODO(Subv): For the time being just forward the same data without any decoding. rgba_data = texture_data; break; diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index f48ca30b8..a17eaf19d 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -122,6 +122,17 @@ enum class ComponentType : u32 { FLOAT = 7 }; +enum class SwizzleSource : u32 { + Zero = 0, + + R = 2, + G = 3, + B = 4, + A = 5, + OneInt = 6, + OneFloat = 7, +}; + union TextureHandle { u32 raw; BitField<0, 20, u32> tic_id; @@ -139,6 +150,11 @@ struct TICEntry { BitField<10, 3, ComponentType> g_type; BitField<13, 3, ComponentType> b_type; BitField<16, 3, ComponentType> a_type; + + BitField<19, 3, SwizzleSource> x_source; + BitField<22, 3, SwizzleSource> y_source; + BitField<25, 3, SwizzleSource> z_source; + BitField<28, 3, SwizzleSource> w_source; }; u32 address_low; union { |