summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp38
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h4
-rw-r--r--src/core/gdbstub/gdbstub.cpp170
-rw-r--r--src/core/gdbstub/gdbstub.h9
-rw-r--r--src/core/hle/service/am/am.cpp13
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/nfp/nfp.cpp108
-rw-r--r--src/core/hle/service/nifm/nifm.cpp11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp22
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h31
-rw-r--r--src/video_core/engines/maxwell_3d.cpp34
-rw-r--r--src/video_core/engines/maxwell_3d.h3
-rw-r--r--src/video_core/engines/shader_bytecode.h134
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/gpu.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp102
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp84
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp272
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h58
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp19
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_state.h6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h23
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp1
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/video_core/textures/texture.h16
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(&params, input.data(), input.size());
+ // TODO : To implement properly
+ std::memcpy(output.data(), &params, 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(&params, input.data(), input.size());
+ // TODO : To implement properly
+ std::memcpy(output.data(), &params, 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 {