Compare commits

..

2 Commits

Author SHA1 Message Date
greggameplayer
492e7e74c7 Merge branch 'master' into patch-8 2018-05-27 23:58:57 +02:00
greggameplayer
38bc85cf65 add all the known TextureFormat 2018-05-26 14:03:13 +02:00
59 changed files with 371 additions and 2092 deletions

View File

@@ -1,7 +1,7 @@
#!/bin/bash -ex
apt-get update
apt-get install -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget ninja-build
apt-get install -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget
# Get a recent version of CMake
wget https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.sh
@@ -10,7 +10,7 @@ sh cmake-3.10.1-Linux-x86_64.sh --exclude-subdir --prefix=/ --skip-license
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
make -j4
ctest -VV -C Release

View File

@@ -35,13 +35,11 @@ namespace Log {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, BCAT) \
SUB(Service, Fatal) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, HID) \
SUB(Service, LM) \
SUB(Service, MM) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NS) \

View File

@@ -55,13 +55,11 @@ enum class Class : ClassType {
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_Fatal, ///< The Fatal service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_HID, ///< The HID (Human interface device) service
Service_LM, ///< The LM (Logger) service
Service_MM, ///< The MM (Multimedia) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NS, ///< The NS services

View File

@@ -124,10 +124,6 @@ add_library(core STATIC
hle/service/audio/audren_u.h
hle/service/audio/codecctl.cpp
hle/service/audio/codecctl.h
hle/service/bcat/module.cpp
hle/service/bcat/module.h
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
@@ -148,8 +144,6 @@ add_library(core STATIC
hle/service/hid/hid.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/nifm/nifm.cpp
hle/service/nifm/nifm.h
hle/service/nifm/nifm_a.cpp
@@ -177,8 +171,6 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
hle/service/nvdrv/devices/nvhost_gpu.cpp
hle/service/nvdrv/devices/nvhost_gpu.h
hle/service/nvdrv/devices/nvhost_nvdec.cpp
hle/service/nvdrv/devices/nvhost_nvdec.h
hle/service/nvdrv/devices/nvmap.cpp
hle/service/nvdrv/devices/nvmap.h
hle/service/nvdrv/interface.cpp

View File

@@ -35,17 +35,6 @@ 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));
@@ -78,10 +67,6 @@ 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() {
@@ -170,11 +155,7 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
}
void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
void ARM_Unicorn::Step() {
@@ -187,18 +168,6 @@ 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) {
@@ -264,8 +233,3 @@ void ARM_Unicorn::PrepareReschedule() {
}
void ARM_Unicorn::ClearInstructionCache() {}
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt = bkpt;
last_bkpt_hit = true;
}

View File

@@ -7,7 +7,6 @@
#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:
@@ -36,10 +35,7 @@ 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;
};

View File

@@ -44,14 +44,14 @@ Cpu& System::CurrentCpuCore() {
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core = 4];
return *cpu_cores[active_core];
}
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[4];
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
@@ -68,7 +68,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
for (active_core = 4; active_core < NUM_CPU_CORES; ++active_core) {
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode

View File

@@ -32,13 +32,9 @@
#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"
@@ -141,17 +137,15 @@ 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.
@@ -171,53 +165,6 @@ 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.
*
@@ -246,7 +193,7 @@ static u8 NibbleToHex(u8 n) {
if (n < 0xA) {
return '0' + n;
} else {
return 'a' + n - 0xA;
return 'A' + n - 0xA;
}
}
@@ -492,8 +439,6 @@ 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));
@@ -538,22 +483,6 @@ 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("");
}
@@ -561,40 +490,11 @@ static void HandleQuery() {
/// Handle set thread command from gdb client.
static void HandleSetThread() {
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;
}
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");
}
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");
}
@@ -603,24 +503,15 @@ static void HandleThreadAlive() {
*
* @param signal Signal to be sent to client.
*/
static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
static void SendSignal(u32 signal) {
if (gdbserver_socket == -1) {
return;
}
latest_signal = signal;
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());
std::string buffer = fmt::format("T{:02x}", latest_signal);
NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer);
SendReply(buffer.c_str());
}
@@ -636,7 +527,7 @@ static void ReadCommand() {
} else if (c == 0x03) {
NGLOG_INFO(Debug_GDBStub, "gdb: found break command");
halt_loop = true;
SendSignal(current_thread, SIGTRAP);
SendSignal(SIGTRAP);
return;
} else if (c != GDB_STUB_START) {
NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
@@ -707,11 +598,11 @@ static void ReadRegister() {
}
if (id <= SP_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast<int>(id)));
} else if (id == PC_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
LongToGdbHex(reply, Core::CurrentArmInterface().GetPC());
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR());
} else {
return SendReply("E01");
}
@@ -727,16 +618,16 @@ static void ReadRegisters() {
u8* bufptr = buffer;
for (int reg = 0; reg <= SP_REGISTER; reg++) {
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg));
}
bufptr += (32 * 16);
LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC());
bufptr += 16;
IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread));
IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR());
bufptr += 8;
@@ -755,11 +646,11 @@ static void WriteRegister() {
}
if (id <= SP_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr));
} else if (id == PC_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr));
} else if (id == CPSR_REGISTER) {
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr));
} else {
return SendReply("E01");
}
@@ -776,11 +667,11 @@ static void WriteRegisters() {
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16));
} else if (reg == PC_REGISTER) {
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16));
} else if (reg == CPSR_REGISTER) {
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16));
} else {
UNIMPLEMENTED();
}
@@ -843,7 +734,7 @@ static void WriteMemory() {
void Break(bool is_memory_break) {
if (!halt_loop) {
halt_loop = true;
send_trap = true;
SendSignal(SIGTRAP);
}
memory_break = is_memory_break;
@@ -853,10 +744,10 @@ void Break(bool is_memory_break) {
static void Step() {
step_loop = true;
halt_loop = true;
send_trap = true;
step_break = true;
SendSignal(SIGTRAP);
}
/// Tell the CPU if we hit a memory breakpoint.
bool IsMemoryBreak() {
if (IsConnected()) {
return false;
@@ -868,6 +759,7 @@ bool IsMemoryBreak() {
/// Tell the CPU to continue executing.
static void Continue() {
memory_break = false;
step_break = false;
step_loop = false;
halt_loop = false;
}
@@ -1006,7 +898,7 @@ void HandlePacket() {
HandleSetThread();
break;
case '?':
SendSignal(current_thread, latest_signal);
SendSignal(latest_signal);
break;
case 'k':
Shutdown();
@@ -1043,9 +935,6 @@ void HandlePacket() {
case 'Z':
AddBreakpoint();
break;
case 'T':
HandleThreadAlive();
break;
default:
SendReply("");
break;
@@ -1190,11 +1079,4 @@ 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

View File

@@ -7,7 +7,6 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/thread.h"
namespace GDBStub {
@@ -92,12 +91,4 @@ 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

View File

@@ -29,10 +29,7 @@ enum class ControlCommand : u32 {
};
enum class CommandType : u32 {
Invalid = 0,
LegacyRequest = 1,
Close = 2,
LegacyControl = 3,
Request = 4,
Control = 5,
RequestWithContext = 6,

View File

@@ -21,9 +21,7 @@ enum {
// Confirmed Switch OS error codes
MisalignedAddress = 102,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
TooLarge = 119,

View File

@@ -732,7 +732,7 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
}
static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
NGLOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
NGLOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:08X}, core=0x{:X}", thread_handle,
mask, core);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
@@ -740,31 +740,6 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
return ERR_INVALID_HANDLE;
}
if (core == THREADPROCESSORID_DEFAULT) {
ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
// Set the target CPU to the one specified in the process' exheader.
core = thread->owner_process->ideal_processor;
mask = 1 << core;
}
if (mask == 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
}
/// This value is used to only change the affinity mask without changing the current ideal core.
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
if (core == OnlyChangeMask) {
core = thread->ideal_core;
} else if (core >= Core::NUM_CPU_CORES && core != -1) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
}
// Error out if the input core isn't enabled in the input mask.
if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
}
thread->ChangeCore(core, mask);
return RESULT_SUCCESS;

View File

@@ -133,11 +133,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner) {
lock_owner->RemoveMutexWaiter(thread);
}
// and don't have a lock owner.
ASSERT(lock_owner == nullptr);
}
if (resume)
@@ -463,13 +460,13 @@ void Thread::UpdatePriority() {
void Thread::ChangeCore(u32 core, u64 mask) {
ideal_core = core;
affinity_mask = mask;
mask = mask;
if (status != THREADSTATUS_READY) {
return;
}
boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
boost::optional<s32> new_processor_id{GetNextProcessorId(mask)};
if (!new_processor_id) {
new_processor_id = processor_id;
@@ -479,7 +476,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
ASSERT(new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <cinttypes>
#include <stack>
#include "core/file_sys/filesystem.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
@@ -155,7 +154,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
RegisterHandlers(functions);
launchable_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
Kernel::Event::Create(Kernel::ResetType::OneShot, "ISelfController:LaunchableEvent");
}
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
@@ -349,100 +348,19 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, &IStorageAccessor::Write, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
NGLOG_DEBUG(Service_AM, "called");
}
void Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
const std::vector<u8> data{ctx.ReadBuffer()};
ASSERT(offset + data.size() <= buffer.size());
std::memcpy(&buffer[offset], data.data(), data.size());
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
const size_t size{ctx.GetWriteBufferSize()};
ASSERT(offset + size <= buffer.size());
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
}
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, nullptr, "OpenTransferStorage"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
NGLOG_DEBUG(Service_AM, "called");
}
};
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
{1, nullptr, "IsCompleted"},
{10, &ILibraryAppletAccessor::Start, "Start"},
{10, nullptr, "Start"},
{20, nullptr, "RequestExit"},
{25, nullptr, "Terminate"},
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
{30, nullptr, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
{100, nullptr, "PushInData"},
{101, nullptr, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
{103, nullptr, "PushInteractiveInData"},
{104, nullptr, "PopInteractiveOutData"},
@@ -470,41 +388,6 @@ private:
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
void GetResult(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
void Start(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
void PushInData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called");
}
void PopOutData(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
storage_stack.pop();
NGLOG_DEBUG(Service_AM, "called");
}
std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
Kernel::SharedPtr<Kernel::Event> state_changed_event;
};
@@ -513,7 +396,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"},
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
{10, nullptr, "CreateStorage"},
{11, nullptr, "CreateTransferMemoryStorage"},
{12, nullptr, "CreateHandleStorage"},
};
@@ -529,17 +412,72 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
NGLOG_DEBUG(Service_AM, "called");
}
void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size{rp.Pop<u64>()};
std::vector<u8> buffer(size);
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, nullptr, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
private:
std::vector<u8> buffer;
NGLOG_DEBUG(Service_AM, "called, size={}", size);
}
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
NGLOG_DEBUG(Service_AM, "called");
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 offset = rp.Pop<u64>();
const size_t size{ctx.GetWriteBufferSize()};
ASSERT(offset + size <= buffer.size());
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called");
}
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, nullptr, "OpenTransferStorage"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
NGLOG_DEBUG(Service_AM, "called");
}
};
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
static const FunctionInfo functions[] = {
@@ -561,7 +499,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{32, nullptr, "BeginBlockingHomeButton"},
{33, nullptr, "EndBlockingHomeButton"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{50, nullptr, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
{65, nullptr, "IsGamePlayRecordingSupported"},
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
@@ -684,17 +622,6 @@ 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);

View File

@@ -121,7 +121,6 @@ public:
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
void CreateStorage(Kernel::HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -138,7 +137,6 @@ 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> {

View File

@@ -1,16 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/bcat/bcat.h"
namespace Service::BCAT {
BCAT::BCAT(std::shared_ptr<Module> module, const char* name)
: Module::Interface(std::move(module), name) {
static const FunctionInfo functions[] = {
{0, &BCAT::CreateBcatService, "CreateBcatService"},
};
RegisterHandlers(functions);
}
} // namespace Service::BCAT

View File

@@ -1,16 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/bcat/module.h"
namespace Service::BCAT {
class BCAT final : public Module::Interface {
public:
explicit BCAT(std::shared_ptr<Module> module, const char* name);
};
} // namespace Service::BCAT

View File

@@ -1,53 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bcat/module.h"
namespace Service::BCAT {
class IBcatService final : public ServiceFramework<IBcatService> {
public:
IBcatService() : ServiceFramework("IBcatService") {
static const FunctionInfo functions[] = {
{10100, nullptr, "RequestSyncDeliveryCache"},
{10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"},
{10200, nullptr, "CancelSyncDeliveryCacheRequest"},
{20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
{20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
{30100, nullptr, "SetPassphrase"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90200, nullptr, "GetDeliveryList"},
{90201, nullptr, "ClearDeliveryCacheStorage"},
{90300, nullptr, "GetPushNotificationLog"},
};
RegisterHandlers(functions);
}
};
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IBcatService>();
NGLOG_DEBUG(Service_BCAT, "called");
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager);
std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager);
std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager);
std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager);
}
} // namespace Service::BCAT

View File

@@ -1,27 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::BCAT {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name);
void CreateBcatService(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
};
};
/// Registers all BCAT services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Service::BCAT

View File

@@ -329,7 +329,6 @@ public:
{130, nullptr, "SwapNpadAssignment"},
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -337,41 +336,12 @@ public:
{204, nullptr, "PermitVibration"},
{205, nullptr, "IsVibrationPermitted"},
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
{207, nullptr, "SendVibrationGcErmCommand"},
{208, nullptr, "GetActualVibrationGcErmCommand"},
{209, nullptr, "BeginPermitVibrationSession"},
{210, nullptr, "EndPermitVibrationSession"},
{300, nullptr, "ActivateConsoleSixAxisSensor"},
{301, nullptr, "StartConsoleSixAxisSensor"},
{302, nullptr, "StopConsoleSixAxisSensor"},
{303, nullptr, "ActivateSevenSixAxisSensor"},
{304, nullptr, "StartSevenSixAxisSensor"},
{305, nullptr, "StopSevenSixAxisSensor"},
{306, nullptr, "InitializeSevenSixAxisSensor"},
{307, nullptr, "FinalizeSevenSixAxisSensor"},
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
{401, nullptr, "EnableUsbFullKeyController"},
{402, nullptr, "IsUsbFullKeyControllerConnected"},
{403, nullptr, "HasBattery"},
{404, nullptr, "HasLeftRightBattery"},
{405, nullptr, "GetNpadInterfaceType"},
{406, nullptr, "GetNpadLeftRightInterfaceType"},
{500, nullptr, "GetPalmaConnectionHandle"},
{501, nullptr, "InitializePalma"},
{502, nullptr, "AcquirePalmaOperationCompleteEvent"},
{503, nullptr, "GetPalmaOperationInfo"},
{504, nullptr, "PlayPalmaActivity"},
{505, nullptr, "SetPalmaFrModeType"},
{506, nullptr, "ReadPalmaStep"},
{507, nullptr, "EnablePalmaStep"},
{508, nullptr, "SuspendPalmaStep"},
{509, nullptr, "ResetPalmaStep"},
{510, nullptr, "ReadPalmaApplicationSection"},
{511, nullptr, "WritePalmaApplicationSection"},
{512, nullptr, "ReadPalmaUniqueCode"},
{513, nullptr, "SetPalmaUniqueCodeInvalid"},
{1000, nullptr, "SetNpadCommunicationMode"},
{1001, nullptr, "GetNpadCommunicationMode"},
};

View File

@@ -1,50 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/service/mm/mm_u.h"
namespace Service::MM {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<MM_U>()->InstallAsService(service_manager);
}
void MM_U::Initialize(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
min = rp.Pop<u32>();
max = rp.Pop<u32>();
current = min;
NGLOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void MM_U::Get(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(current);
}
MM_U::MM_U() : ServiceFramework("mm:u") {
static const FunctionInfo functions[] = {
{0, nullptr, "InitializeOld"}, {1, nullptr, "FinalizeOld"},
{2, nullptr, "SetAndWaitOld"}, {3, nullptr, "GetOld"},
{4, &MM_U::Initialize, "Initialize"}, {5, nullptr, "Finalize"},
{6, &MM_U::SetAndWait, "SetAndWait"}, {7, &MM_U::Get, "Get"},
};
RegisterHandlers(functions);
}
} // namespace Service::MM

View File

@@ -1,29 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::MM {
class MM_U final : public ServiceFramework<MM_U> {
public:
MM_U();
~MM_U() = default;
private:
void Initialize(Kernel::HLERequestContext& ctx);
void SetAndWait(Kernel::HLERequestContext& ctx);
void Get(Kernel::HLERequestContext& ctx);
u32 min{0};
u32 max{0};
u32 current{0};
};
/// Registers all MM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Service::MM

View File

@@ -4,8 +4,6 @@
#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"
@@ -20,7 +18,7 @@ public:
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, nullptr, "Finalize"},
{2, &IUser::ListDevices, "ListDevices"},
{2, nullptr, "ListDevices"},
{3, nullptr, "StartDetection"},
{4, nullptr, "StopDetection"},
{5, nullptr, "Mount"},
@@ -35,116 +33,24 @@ public:
{14, nullptr, "GetRegisterInfo"},
{15, nullptr, "GetCommonInfo"},
{16, nullptr, "GetModelInfo"},
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{19, &IUser::GetState, "GetState"},
{20, &IUser::GetDeviceState, "GetDeviceState"},
{21, &IUser::GetNpadId, "GetNpadId"},
{17, nullptr, "AttachActivateEvent"},
{18, nullptr, "AttachDeactivateEvent"},
{19, nullptr, "GetState"},
{20, nullptr, "GetDeviceState"},
{21, nullptr, "GetNpadId"},
{22, nullptr, "GetApplicationArea2"},
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{23, nullptr, "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) {

View File

@@ -38,7 +38,7 @@ public:
{8, nullptr, "SetPriority"},
{9, nullptr, "SetNetworkProfileId"},
{10, nullptr, "SetRejectable"},
{11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"},
{11, nullptr, "SetConnectionConfirmationOption"},
{12, nullptr, "SetPersistent"},
{13, nullptr, "SetInstant"},
{14, nullptr, "SetSustainable"},
@@ -67,32 +67,23 @@ 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;
};

View File

@@ -16,11 +16,7 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
case IoctlCommand::IocGetConfigCommand:
return NvOsGetConfigU32(input, output);
case IoctlCommand::IocCtrlEventWaitCommand:
return IocCtrlEventWait(input, output, false);
case IoctlCommand::IocCtrlEventWaitAsyncCommand:
return IocCtrlEventWait(input, output, true);
case IoctlCommand::IocCtrlEventRegisterCommand:
return IocCtrlEventRegister(input, output);
return IocCtrlEventWait(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
@@ -49,13 +45,11 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
bool is_async) {
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
NGLOG_WARNING(Service_NVDRV,
"(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}",
params.syncpt_id, params.threshold, params.timeout, is_async);
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id={} threshold={} timeout={}",
params.syncpt_id, params.threshold, params.timeout);
// TODO(Subv): Implement actual syncpt waiting.
params.value = 0;
@@ -63,10 +57,4 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
// TODO(bunnei): Implement this.
return 0;
}
} // namespace Service::Nvidia::Devices

View File

@@ -134,9 +134,7 @@ private:
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -26,10 +26,6 @@ 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;
@@ -140,22 +136,4 @@ 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

View File

@@ -26,18 +26,6 @@ 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 {
@@ -139,31 +127,12 @@ 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

View File

@@ -26,19 +26,11 @@ public:
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocAllocGPFIFOCommand = 0x40084805,
IocSetClientDataCommand = 0x40084714,
IocGetClientDataCommand = 0x80084715,
IocZCullBind = 0xc010480b,
IocSetErrorNotifierCommand = 0xC018480C,
IocChannelSetPriorityCommand = 0x4004480D,
IocEnableCommand = 0x0000480E,
IocDisableCommand = 0x0000480F,
IocPreemptCommand = 0x00004810,
IocForceResetCommand = 0x00004811,
IocEventIdControlCommand = 0x40084812,
IocGetErrorNotificationCommand = 0xC0104817,
IocAllocGPFIFOExCommand = 0x40204818,
IocAllocGPFIFOEx2Command = 0xC020481A,
IocAllocObjCtxCommand = 0xC0104809,
IocChannelGetWaitbaseCommand = 0xC0080003,
@@ -64,12 +56,6 @@ private:
};
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
struct IoctlAllocGPFIFO {
u32_le num_entries;
u32_le flags;
};
static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
struct IoctlClientData {
u64_le data;
};
@@ -90,45 +76,12 @@ private:
};
static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
struct IoctlChannelSetPriority {
u32_le priority;
};
static_assert(sizeof(IoctlChannelSetPriority) == 4,
"IoctlChannelSetPriority is incorrect size");
struct IoctlEventIdControl {
u32_le cmd; // 0=disable, 1=enable, 2=clear
u32_le id;
};
static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
struct IoctlGetErrorNotification {
u64_le timestamp;
u32_le info32;
u16_le info16;
u16_le status; // always 0xFFFF
};
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
struct IoctlFence {
u32_le id;
u32_le value;
};
static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
struct IoctlAllocGpfifoEx {
u32_le num_entries;
u32_le flags;
u32_le unk0;
u32_le unk1;
u32_le unk2;
u32_le unk3;
u32_le unk4;
u32_le unk5;
};
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
struct IoctlAllocGpfifoEx2 {
u32_le num_entries; // in
u32_le flags; // in

View File

@@ -1,32 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
namespace Service::Nvidia::Devices {
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
}
} // namespace Service::Nvidia::Devices

View File

@@ -1,38 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstdlib>
#include <cstring>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service::Nvidia::Devices {
class nvhost_nvdec final : public nvdevice {
public:
nvhost_nvdec() = default;
~nvhost_nvdec() override = default;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
u32_le nvmap_fd{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -9,7 +9,6 @@
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdrv.h"
@@ -37,7 +36,6 @@ Module::Module() {
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>();
}
u32 Module::Open(std::string device_name) {

View File

@@ -20,13 +20,11 @@
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/mm/mm_u.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/ns/ns.h"
@@ -185,14 +183,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
Fatal::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(*sm);
Friend::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);
LM::InstallInterfaces(*sm);
MM::InstallInterfaces(*sm);
NFP::InstallInterfaces(*sm);
NIFM::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm);

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <chrono>
#include <ctime>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -78,7 +77,7 @@ public:
{3, nullptr, "LoadLocationNameList"},
{4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
{5, nullptr, "GetTimeZoneRuleVersion"},
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
{100, nullptr, "ToCalendarTime"},
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
{200, nullptr, "ToPosixTime"},
{201, nullptr, "ToPosixTimeWithMyRule"},
@@ -87,11 +86,9 @@ public:
}
private:
LocationName location_name{"UTC"};
TimeZoneRule my_time_zone_rule{};
void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
NGLOG_DEBUG(Service_Time, "called");
NGLOG_WARNING(Service_Time, "(STUBBED) called");
LocationName location_name{};
IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(location_name);
@@ -106,70 +103,23 @@ private:
void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_Time, "(STUBBED) called");
ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ToCalendarTime(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 posix_time = rp.Pop<u64>();
NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
TimeZoneRule time_zone_rule{};
auto buffer = ctx.ReadBuffer();
std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
CalendarAdditionalInfo additional_info{};
PosixToCalendar(posix_time, calendar_time, additional_info, time_zone_rule);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(calendar_time);
rb.PushRaw(additional_info);
}
void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 posix_time = rp.Pop<u64>();
u64 posix_time = rp.Pop<u64>();
NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
CalendarAdditionalInfo additional_info{};
PosixToCalendar(posix_time, calendar_time, additional_info, my_time_zone_rule);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(calendar_time);
rb.PushRaw(additional_info);
}
void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) {
std::time_t t(posix_time);
std::tm* tm = std::localtime(&t);
if (!tm) {
return;
}
calendar_time.year = tm->tm_year + 1900;
calendar_time.month = tm->tm_mon + 1;
calendar_time.day = tm->tm_mday;
calendar_time.hour = tm->tm_hour;
calendar_time.minute = tm->tm_min;
calendar_time.second = tm->tm_sec;
additional_info.day_of_week = tm->tm_wday;
additional_info.day_of_year = tm->tm_yday;
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
additional_info.utc_offset = 0;
}
};
void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {

View File

@@ -4,13 +4,13 @@
#pragma once
#include <array>
#include "core/hle/service/service.h"
namespace Service::Time {
// TODO(Rozelette) RE this structure
struct LocationName {
std::array<u8, 0x24> name;
INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size");
@@ -25,34 +25,26 @@ struct CalendarTime {
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
// TODO(Rozelette) RE this structure
struct CalendarAdditionalInfo {
u32_le day_of_week;
u32_le day_of_year;
std::array<u8, 8> name;
INSERT_PADDING_BYTES(1);
s32_le utc_offset;
INSERT_PADDING_BYTES(0x18);
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
"CalendarAdditionalInfo structure has incorrect size");
// TODO(mailwl) RE this structure
struct TimeZoneRule {
INSERT_PADDING_BYTES(0x4000);
};
struct SteadyClockTimePoint {
u64_le value;
INSERT_PADDING_WORDS(4);
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
// TODO(bunnei) RE this structure
struct SystemClockContext {
u64_le offset;
SteadyClockTimePoint time_point;
INSERT_PADDING_BYTES(0x20);
};
static_assert(sizeof(SystemClockContext) == 0x20,
"SystemClockContext structure has incorrect size");
struct SteadyClockTimePoint {
u64 value;
INSERT_PADDING_WORDS(4);
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
class Module final {
public:
class Interface : public ServiceFramework<Interface> {

View File

@@ -156,15 +156,16 @@ void Maxwell3D::ProcessQueryGet() {
// TODO(Subv): Support the other query units.
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
"Units other than CROP are unimplemented");
ASSERT_MSG(regs.query.query_get.short_query,
"Writing the entire query result structure is unimplemented");
u32 value = Memory::Read32(*address);
u64 result = 0;
u32 result = 0;
// TODO(Subv): Support the other query variables
switch (regs.query.query_get.select) {
case Regs::QuerySelect::Zero:
// This seems to actually write the query sequence to the query address.
result = regs.query.query_sequence;
result = 0;
break;
default:
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
@@ -173,31 +174,15 @@ void Maxwell3D::ProcessQueryGet() {
// TODO(Subv): Research and implement how query sync conditions work.
struct LongQueryResult {
u64_le value;
u64_le timestamp;
};
static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size");
switch (regs.query.query_get.mode) {
case Regs::QueryMode::Write:
case Regs::QueryMode::Write2: {
// Write the current query sequence to the sequence address.
u32 sequence = regs.query.query_sequence;
if (regs.query.query_get.short_query) {
// Write the current query sequence to the sequence address.
// TODO(Subv): Find out what happens if you use a long query type but mark it as a short
// query.
Memory::Write32(*address, sequence);
} else {
// Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
// GPU, this command may actually take a while to complete in real hardware due to GPU
// wait queues.
LongQueryResult query_result{};
query_result.value = result;
// TODO(Subv): Generate a real GPU timestamp and write it here instead of 0
query_result.timestamp = 0;
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
}
Memory::Write32(*address, sequence);
// TODO(Subv): Write the proper query response structure to the address when not using short
// mode.
break;
}
default:
@@ -354,40 +339,6 @@ 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];

View File

@@ -354,35 +354,10 @@ public:
f32 scale_x;
f32 scale_y;
f32 scale_z;
f32 translate_x;
f32 translate_y;
f32 translate_z;
u32 translate_x;
u32 translate_y;
u32 translate_z;
INSERT_PADDING_WORDS(2);
MathUtil::Rectangle<s32> GetRect() const {
return {
GetX(), // left
GetY() + GetHeight(), // top
GetX() + GetWidth(), // right
GetY() // bottom
};
};
s32 GetX() const {
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
}
s32 GetY() const {
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
}
s32 GetWidth() const {
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
}
s32 GetHeight() const {
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
}
} viewport_transform[NumViewports];
struct {
@@ -396,6 +371,15 @@ public:
};
float depth_range_near;
float depth_range_far;
MathUtil::Rectangle<s32> GetRect() const {
return {
static_cast<s32>(x), // left
static_cast<s32>(y + height), // top
static_cast<s32>(x + width), // right
static_cast<s32>(y) // bottom
};
};
} viewport[NumViewports];
INSERT_PADDING_WORDS(0x1D);
@@ -664,9 +648,6 @@ 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;

View File

@@ -75,10 +75,6 @@ union Attribute {
enum class Index : u64 {
Position = 7,
Attribute_0 = 8,
// This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
// shader.
TessCoordInstanceIDVertexID = 47,
};
union {
@@ -109,6 +105,11 @@ union Sampler {
u64 value{};
};
union Uniform {
BitField<20, 14, u64> offset;
BitField<34, 5, u64> index;
};
} // namespace Shader
} // namespace Tegra
@@ -151,13 +152,6 @@ enum class PredOperation : u64 {
Xor = 2,
};
enum class LogicOperation : u64 {
And = 0,
Or = 1,
Xor = 2,
PassB = 3,
};
enum class SubOp : u64 {
Cos = 0x0,
Sin = 0x1,
@@ -168,31 +162,6 @@ 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;
@@ -229,12 +198,6 @@ union Instruction {
BitField<42, 1, u64> negate_pred;
} fmnmx;
union {
BitField<53, 2, LogicOperation> operation;
BitField<55, 1, u64> invert_a;
BitField<56, 1, u64> invert_b;
} lop;
float GetImm20_19() const {
float result{};
u32 imm{static_cast<u32>(imm20_19)};
@@ -250,42 +213,13 @@ 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<48, 1, u64> negate_b;
BitField<49, 1, u64> negate_a;
} iscadd;
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;
u64 GetLeftShiftValue() const {
return 32 - (shift_position + shift_length);
}
} bfe;
union {
BitField<48, 1, u64> negate_b;
BitField<49, 1, u64> negate_c;
} 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;
@@ -300,16 +234,6 @@ union Instruction {
BitField<56, 1, u64> neg_b;
} fsetp;
union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
BitField<39, 3, u64> pred39;
BitField<42, 1, u64> neg_pred;
BitField<45, 2, PredOperation> op;
BitField<48, 1, u64> is_signed;
BitField<49, 3, PredCondition> cond;
} isetp;
union {
BitField<39, 3, u64> pred39;
BitField<42, 1, u64> neg_pred;
@@ -317,87 +241,28 @@ union Instruction {
BitField<44, 1, u64> abs_b;
BitField<45, 2, PredOperation> op;
BitField<48, 4, PredCondition> cond;
BitField<52, 1, u64> bf;
BitField<53, 1, u64> neg_b;
BitField<54, 1, u64> abs_a;
BitField<52, 1, u64> bf;
BitField<55, 1, u64> ftz;
BitField<56, 1, u64> neg_imm;
} fset;
union {
BitField<10, 2, Register::Size> size;
BitField<12, 1, u64> is_output_signed;
BitField<13, 1, u64> is_input_signed;
BitField<13, 1, u64> is_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 {
BitField<31, 4, u64> component_mask;
bool IsComponentEnabled(size_t component) const {
return ((1 << component) & component_mask) != 0;
}
} tex;
union {
BitField<50, 3, u64> component_mask_selector;
BitField<28, 8, Register> gpr28;
bool HasTwoDestinations() const {
return gpr28.Value() != Register::ZeroIndex;
}
bool IsComponentEnabled(size_t component) const {
static constexpr std::array<size_t, 5> one_dest_mask{0x1, 0x2, 0x4, 0x8, 0x3};
static constexpr std::array<size_t, 5> two_dest_mask{0x7, 0xb, 0xd, 0xe, 0xf};
const auto& mask{HasTwoDestinations() ? two_dest_mask : one_dest_mask};
ASSERT(component_mask_selector < mask.size());
return ((1 << component) & mask[component_mask_selector]) != 0;
}
} texs;
union {
BitField<20, 24, u64> target;
BitField<5, 1, u64> constant_buffer;
s32 GetBranchTarget() const {
// Sign extend the branch target offset
u32 mask = 1U << (24 - 1);
u32 value = static_cast<u32>(target);
// 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;
@@ -410,14 +275,8 @@ class OpCode {
public:
enum class Id {
KIL,
BFE_C,
BFE_R,
BFE_IMM,
BRA,
LD_A,
LD_C,
ST_A,
TEX,
TEXQ, // Texture Query
TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
TLDS, // Texture Load with scalar/non-vec4 source/destinations
@@ -434,13 +293,8 @@ public:
FMUL_R,
FMUL_IMM,
FMUL32_IMM,
ISCADD_C, // Scale and Add
ISCADD_R,
ISCADD_IMM,
MUFU, // Multi-Function Operator
RRO_C, // Range Reduction Operator
RRO_R,
RRO_IMM,
MUFU, // Multi-Function Operator
RRO, // Range Reduction Operator
F2F_C,
F2F_R,
F2F_IMM,
@@ -458,18 +312,12 @@ 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,
@@ -480,19 +328,11 @@ 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,
Memory,
@@ -596,11 +436,8 @@ private:
std::vector<Matcher> table = {
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
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"),
INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
@@ -617,39 +454,25 @@ private:
INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"),
INST("0100110000011---", Id::ISCADD_C, Type::ScaledAdd, "ISCADD_C"),
INST("0101110000011---", Id::ISCADD_R, Type::ScaledAdd, "ISCADD_R"),
INST("0011100-00011---", Id::ISCADD_IMM, Type::ScaledAdd, "ISCADD_IMM"),
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
INST("0011100-10010---", Id::RRO_IMM, Type::Arithmetic, "RRO_IMM"),
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::Conversion, "F2I_C"),
INST("0101110010110---", Id::F2I_R, Type::Conversion, "F2I_R"),
INST("0011100-10110---", Id::F2I_IMM, Type::Conversion, "F2I_IMM"),
INST("0101110010010---", Id::RRO, Type::Arithmetic, "RRO"),
INST("0100110010101---", Id::F2F_C, Type::Arithmetic, "F2F_C"),
INST("0101110010101---", Id::F2F_R, Type::Arithmetic, "F2F_R"),
INST("0011100-10101---", Id::F2F_IMM, Type::Arithmetic, "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("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"),
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"),
@@ -666,10 +489,6 @@ 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) {

View File

@@ -26,10 +26,6 @@ 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;

View File

@@ -15,12 +15,10 @@ 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.

View File

@@ -196,10 +196,8 @@ 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 bindpoints to use when uploading the const buffers and textures to the GLSL
// shaders.
// Next available bindpoint to use when uploading the const buffers 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];
@@ -214,14 +212,13 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
continue;
}
GLShader::MaxwellUniformData ubo{};
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
// Upload uniform data as one UBO per stage
const GLintptr ubo_offset = buffer_offset;
copy_buffer(uniform_buffers[stage].handle, ubo_offset,
sizeof(GLShader::MaxwellUniformData));
GLShader::MaxwellUniformData* ub_ptr =
reinterpret_cast<GLShader::MaxwellUniformData*>(buffer_ptr);
ub_ptr->SetFromRegs(gpu.state.shader_stages[stage]);
buffer_ptr += sizeof(GLShader::MaxwellUniformData);
buffer_offset += sizeof(GLShader::MaxwellUniformData);
@@ -261,11 +258,6 @@ 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();
@@ -306,7 +298,7 @@ void RasterizerOpenGL::DrawArrays() {
const bool has_stencil = false;
const bool using_color_fb = true;
const bool using_depth_fb = false;
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()};
const bool write_color_fb =
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
@@ -349,6 +341,9 @@ 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;
@@ -452,6 +447,39 @@ 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) {
@@ -626,16 +654,7 @@ 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;
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));
}
std::vector<u8> data(used_buffer.GetSize() * sizeof(float));
Memory::ReadBlock(*addr, data.data(), data.size());
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo);
@@ -655,52 +674,6 @@ 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;
@@ -729,7 +702,7 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()};
state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale;
state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale;

View File

@@ -80,6 +80,9 @@ 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.
@@ -92,17 +95,6 @@ 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);

View File

@@ -45,26 +45,20 @@ struct FormatTuple {
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false}, // B5G6R5
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false}, // B5G6R5
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10
{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
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1
}};
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
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 either R11FG11FB10F or RGBA16F which are
// type FLOAT
ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F ||
pixel_format == PixelFormat::R11FG11FB10F);
// For now only UNORM components are supported
ASSERT(component_type == ComponentType::UNorm);
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
// TODO(Subv): Implement depth formats
@@ -113,12 +107,10 @@ 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::R11FG11FB10F>, 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::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
MortonCopy<true, PixelFormat::DXT45>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr,
@@ -129,11 +121,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
MortonCopy<false, PixelFormat::B5G6R5>,
MortonCopy<false, PixelFormat::A2B10G10R10>,
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,
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45 formats is not yet supported
nullptr,
nullptr,
nullptr,
@@ -169,10 +157,60 @@ 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;
}
@@ -888,8 +926,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
// Use GetSurfaceSubRect instead
ASSERT(params.width == params.stride);
ASSERT(!params.is_tiled ||
(params.GetActualWidth() % 8 == 0 && params.GetActualHeight() % 8 == 0));
ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0));
// Check for an exact match in existing surfaces
Surface surface =
@@ -1052,19 +1089,16 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
params.UpdateParams();
if (params.GetActualWidth() % 8 != 0 || params.GetActualHeight() % 8 != 0 ||
if (config.tic.Width() % 8 != 0 || config.tic.Height() % 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);
auto dst_rect = tmp_surface->GetScaledRect().Scale(params.GetCompresssionFactor());
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, dst_rect,
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
tmp_surface->GetScaledRect(),
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
draw_framebuffer.handle);

View File

@@ -58,13 +58,9 @@ struct SurfaceParams {
B5G6R5 = 1,
A2B10G10R10 = 2,
A1B5G5R5 = 3,
R8 = 4,
RGBA16F = 5,
R11FG11FB10F = 6,
DXT1 = 7,
DXT23 = 8,
DXT45 = 9,
DXN1 = 10, // This is also known as BC4
DXT1 = 4,
DXT23 = 5,
DXT45 = 6,
Max,
Invalid = 255,
@@ -104,13 +100,9 @@ struct SurfaceParams {
1, // B5G6R5
1, // A2B10G10R10
1, // A1B5G5R5
1, // R8
1, // RGBA16F
1, // R11FG11FB10F
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
}};
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
@@ -129,13 +121,9 @@ struct SurfaceParams {
16, // B5G6R5
32, // A2B10G10R10
16, // A1B5G5R5
8, // R8
64, // RGBA16F
32, // R11FG11FB10F
64, // DXT1
128, // DXT23
128, // DXT45
64, // DXN1
}};
ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -152,10 +140,6 @@ struct SurfaceParams {
return PixelFormat::ABGR8;
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
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();
@@ -183,20 +167,12 @@ struct SurfaceParams {
return PixelFormat::A2B10G10R10;
case Tegra::Texture::TextureFormat::A1B5G5R5:
return PixelFormat::A1B5G5R5;
case Tegra::Texture::TextureFormat::R8:
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:
return PixelFormat::DXT23;
case Tegra::Texture::TextureFormat::DXT45:
return PixelFormat::DXT45;
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -214,20 +190,12 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::A2B10G10R10;
case PixelFormat::A1B5G5R5:
return Tegra::Texture::TextureFormat::A1B5G5R5;
case PixelFormat::R8:
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:
return Tegra::Texture::TextureFormat::DXT23;
case PixelFormat::DXT45:
return Tegra::Texture::TextureFormat::DXT45;
case PixelFormat::DXN1:
return Tegra::Texture::TextureFormat::DXN1;
default:
UNREACHABLE();
}
@@ -251,9 +219,6 @@ struct SurfaceParams {
case Tegra::RenderTargetFormat::RGBA8_SRGB:
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));
UNREACHABLE();

View File

@@ -20,6 +20,7 @@ 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;
@@ -87,20 +88,6 @@ private:
return *subroutines.insert(std::move(subroutine)).first;
}
/// Merges exit method of two parallel branches.
static ExitMethod ParallelExit(ExitMethod a, ExitMethod b) {
if (a == ExitMethod::Undetermined) {
return b;
}
if (b == ExitMethod::Undetermined) {
return a;
}
if (a == b) {
return a;
}
return ExitMethod::Conditional;
}
/// Scans a range of code for labels and determines the exit method.
ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) {
auto [iter, inserted] =
@@ -110,27 +97,10 @@ private:
return exit_method;
for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
const Instruction instr = {program_code[offset]};
if (const auto opcode = OpCode::Decode(instr)) {
if (const auto opcode = OpCode::Decode({program_code[offset]})) {
switch (opcode->GetId()) {
case OpCode::Id::EXIT: {
// The EXIT instruction can be predicated, which means that the shader can
// conditionally end on this instruction. We have to consider the case where the
// condition is not met and check the exit method of that other basic block.
using Tegra::Shader::Pred;
if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
return exit_method = ExitMethod::AlwaysEnd;
} else {
ExitMethod not_met = Scan(offset + 1, end, labels);
return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met);
}
}
case OpCode::Id::BRA: {
u32 target = offset + instr.bra.GetBranchTarget();
labels.insert(target);
ExitMethod no_jmp = Scan(offset + 1, end, labels);
ExitMethod jmp = Scan(target, end, labels);
return exit_method = ParallelExit(no_jmp, jmp);
return exit_method = ExitMethod::AlwaysEnd;
}
}
}
@@ -227,11 +197,6 @@ public:
return active_type == Type::Integer;
}
/// Returns the current active type of the register
Type GetActiveType() const {
return active_type;
}
/// Returns the index of the register
size_t GetIndex() const {
return index;
@@ -334,7 +299,7 @@ public:
* are stored as floats, so this may require conversion.
* @param reg The destination register to use.
* @param elem The element to use for the operation.
* @param attribute The input attribute to use as the source value.
* @param attribute The input attibute to use as the source value.
*/
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute) {
std::string dest = GetRegisterAsFloat(reg);
@@ -363,29 +328,16 @@ public:
shader.AddLine(dest + " = " + src + ';');
}
/// Generates code representing a uniform (C buffer) register, interpreted as the input type.
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) + ']';
/// Generates code representing a uniform (C buffer) register.
std::string GetUniform(const Uniform& uniform, const Register& dest_reg) {
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) + ']';
if (type == GLSLRegister::Type::Float) {
if (regs[dest_reg].IsFloat()) {
return value;
} else if (type == GLSLRegister::Type::Integer) {
return "floatBitsToInt(" + value + ')';
} else {
UNREACHABLE();
}
}
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) {
} else if (regs[dest_reg].IsInteger()) {
return "floatBitsToInt(" + value + ')';
} else {
UNREACHABLE();
@@ -431,14 +383,6 @@ 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
@@ -449,32 +393,6 @@ 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 {
@@ -533,12 +451,6 @@ private:
switch (attribute) {
case Attribute::Index::Position:
return "position";
case Attribute::Index::TessCoordInstanceIDVertexID:
// TODO(Subv): Find out what the values are for the first two elements when inside a
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
// shader.
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
return "vec4(0, 0, gl_InstanceID, gl_VertexID)";
default:
const u32 index{static_cast<u32>(attribute) -
static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -584,7 +496,6 @@ 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;
};
@@ -604,7 +515,7 @@ public:
/// Returns entries in the shader that are useful for external functions
ShaderEntries GetEntries() const {
return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()};
return {regs.GetConstBuffersDeclarations()};
}
private:
@@ -626,8 +537,12 @@ private:
}
/// Generates code representing a texture sampler.
std::string GetSampler(const Sampler& sampler) {
return regs.AccessSampler(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) + ']';
}
/**
@@ -691,9 +606,9 @@ private:
std::string GetPredicateComparison(Tegra::Shader::PredCondition condition) const {
using Tegra::Shader::PredCondition;
static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
{PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
{PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
{PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="},
{PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="},
{PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"},
{PredCondition::GreaterEqual, ">="},
};
auto comparison = PredicateComparisonStrings.find(condition);
@@ -784,8 +699,7 @@ private:
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsFloat(instr.gpr20);
} else {
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
}
}
@@ -872,13 +786,8 @@ private:
1, 1);
break;
}
case OpCode::Id::RRO_C:
case OpCode::Id::RRO_R:
case OpCode::Id::RRO_IMM: {
// Currently RRO is only implemented as a register move.
// Usage of `abs_b` and `negate_b` here should also be correct.
regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1);
NGLOG_WARNING(HW_GPU, "RRO instruction is incomplete");
case OpCode::Id::RRO: {
NGLOG_DEBUG(HW_GPU, "Skipping RRO instruction");
break;
}
default: {
@@ -888,129 +797,6 @@ 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, true);
if (instr.alu.lop.invert_a)
op_a = "~(" + op_a + ')';
switch (opcode->GetId()) {
case OpCode::Id::LOP32I: {
u32 imm = static_cast<u32>(instr.alu.imm20_32.Value());
if (instr.alu.lop.invert_b)
imm = ~imm;
switch (instr.alu.lop.operation) {
case Tegra::Shader::LogicOperation::And: {
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + op_a + " & " + std::to_string(imm) + ')', 1, 1);
break;
}
case Tegra::Shader::LogicOperation::Or: {
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + op_a + " | " + std::to_string(imm) + ')', 1, 1);
break;
}
case Tegra::Shader::LogicOperation::Xor: {
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1);
break;
}
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented lop32i operation: {}",
static_cast<u32>(instr.alu.lop.operation.Value()));
UNREACHABLE();
}
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled logic instruction: {}", opcode->GetName());
UNREACHABLE();
}
}
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);
if (instr.iscadd.negate_a)
op_a = '-' + op_a;
std::string op_b = instr.iscadd.negate_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);
}
}
std::string shift = std::to_string(instr.iscadd.shift_amount.Value());
regs.SetRegisterToInteger(instr.gpr0, true, 0,
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
break;
}
case OpCode::Type::Ffma: {
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
std::string op_b = instr.ffma.negate_b ? "-" : "";
@@ -1018,8 +804,7 @@ private:
switch (opcode->GetId()) {
case OpCode::Id::FFMA_CR: {
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
op_c += regs.GetRegisterAsFloat(instr.gpr39);
break;
}
@@ -1030,8 +815,7 @@ private:
}
case OpCode::Id::FFMA_RC: {
op_b += regs.GetRegisterAsFloat(instr.gpr39);
op_c += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
op_c += regs.GetUniform(instr.uniform, instr.gpr0);
break;
}
case OpCode::Id::FFMA_IMM: {
@@ -1050,99 +834,21 @@ private:
}
case OpCode::Type::Conversion: {
ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented");
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented");
ASSERT_MSG(!instr.conversion.saturate_a, "Unimplemented");
switch (opcode->GetId()) {
case OpCode::Id::I2I_R: {
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
std::string op_a =
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_output_signed, 0, op_a, 1,
1);
break;
}
case OpCode::Id::I2I_R:
case OpCode::Id::I2F_R: {
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
std::string op_a =
regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed);
regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
if (instr.conversion.abs_a) {
op_a = "abs(" + op_a + ')';
}
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
break;
}
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 + ')';
}
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);
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1);
break;
}
default: {
@@ -1153,49 +859,25 @@ 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,
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();
}
attribute);
break;
}
case OpCode::Id::ST_A: {
ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested");
regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index,
instr.attribute.fmt20.element, instr.gpr0);
regs.SetOutputAttributeToRegister(attribute, instr.attribute.fmt20.element,
instr.gpr0);
break;
}
case OpCode::Id::TEX: {
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.gpr8.Value() + 1);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
const std::string sampler = GetSampler(instr.sampler);
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
// Add an extra scope and declare the texture coords inside to prevent overwriting
@@ -1204,51 +886,8 @@ private:
++shader.scope;
shader.AddLine(coord);
const std::string texture = "texture(" + sampler + ", coords)";
size_t dest_elem{};
for (size_t elem = 0; elem < 4; ++elem) {
if (!instr.tex.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
++dest_elem;
}
--shader.scope;
shader.AddLine("}");
break;
}
case OpCode::Id::TEXS: {
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);
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{");
++shader.scope;
shader.AddLine(coord);
const std::string texture = "texture(" + sampler + ", coords)";
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA goes
// into gpr28+0 and gpr28+1
size_t offset{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
for (unsigned elem = 0; elem < 2; ++elem) {
if (!instr.texs.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(dest, elem + offset, texture, 1, 4, false, elem);
}
if (!instr.texs.HasTwoDestinations()) {
// Skip the second destination
break;
}
offset += 2;
for (unsigned elem = 0; elem < instr.attribute.fmt20.size; ++elem) {
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, elem);
}
--shader.scope;
shader.AddLine("}");
@@ -1281,8 +920,7 @@ private:
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsFloat(instr.gpr20);
} else {
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
}
}
@@ -1306,51 +944,13 @@ private:
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
if (instr.fsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
// Set the secondary predicate to the result of !Predicate OP SecondPredicate,
// if enabled
// Set the secondary predicate to the result of !Predicate OP SecondPredicate, if
// enabled
SetPredicate(instr.fsetp.pred0,
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
}
break;
}
case OpCode::Type::IntegerSetPredicate: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed);
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, 0, instr.isetp.is_signed);
} else {
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Integer);
}
}
using Tegra::Shader::Pred;
// We can't use the constant predicate as destination.
ASSERT(instr.isetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
std::string second_pred =
GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0);
std::string comparator = GetPredicateComparison(instr.isetp.cond);
std::string combiner = GetPredicateCombiner(instr.isetp.op);
std::string predicate = '(' + op_a + ") " + comparator + " (" + op_b + ')';
// Set the primary predicate to the result of Predicate OP SecondPredicate
SetPredicate(instr.isetp.pred3,
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
if (instr.isetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
// Set the secondary predicate to the result of !Predicate OP SecondPredicate,
// if enabled
SetPredicate(instr.isetp.pred0,
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
}
break;
}
case OpCode::Type::FloatSet: {
std::string op_a = instr.fset.neg_a ? "-" : "";
op_a += regs.GetRegisterAsFloat(instr.gpr8);
@@ -1371,8 +971,7 @@ private:
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsFloat(instr.gpr20);
} else {
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
op_b += regs.GetUniform(instr.uniform, instr.gpr0);
}
}
@@ -1391,17 +990,15 @@ private:
std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " +
combiner + " (" + second_pred + "))";
if (instr.fset.bf) {
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
} else {
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
1);
}
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
break;
}
default: {
switch (opcode->GetId()) {
case OpCode::Id::EXIT: {
ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex),
"Predicated exits not implemented");
// Final color output is currently hardcoded to GPR0-3 for fragment shaders
if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
shader.AddLine("color.r = " + regs.GetRegisterAsFloat(0) + ';');
@@ -1411,25 +1008,13 @@ private:
}
shader.AddLine("return true;");
if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
// If this is an unconditional exit then just end processing here, otherwise we
// have to account for the possibility of the condition not being met, so
// continue processing the next instruction.
offset = PROGRAM_END - 1;
}
offset = PROGRAM_END - 1;
break;
}
case OpCode::Id::KIL: {
shader.AddLine("discard;");
break;
}
case OpCode::Id::BRA: {
ASSERT_MSG(instr.bra.constant_buffer == 0,
"BRA with constant buffers are not implemented");
u32 target = offset + instr.bra.GetBranchTarget();
shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
break;
}
case OpCode::Id::IPA: {
const auto& attribute = instr.attribute.fmt28;
regs.SetRegisterToInputAttibute(instr.gpr0, attribute.element, attribute.index);

View File

@@ -62,6 +62,8 @@ layout (std140) uniform fs_config {
vec4 viewport_flip;
};
uniform sampler2D tex[32];
void main() {
exec_shader();
}

View File

@@ -22,28 +22,17 @@ class ConstBufferEntry {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
public:
void MarkAsUsed(u64 index, u64 offset, Maxwell::ShaderStage stage) {
void MarkAsUsed(unsigned index, unsigned offset, Maxwell::ShaderStage stage) {
is_used = true;
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->index = 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;
}
@@ -62,54 +51,13 @@ 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>;

View File

@@ -32,6 +32,25 @@ 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) {

View File

@@ -45,6 +45,7 @@ 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 {

View File

@@ -50,10 +50,6 @@ 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;
@@ -204,15 +200,6 @@ 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

View File

@@ -85,12 +85,6 @@ 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 {

View File

@@ -100,8 +100,6 @@ 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:
@@ -180,25 +178,4 @@ 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

View File

@@ -316,7 +316,6 @@ 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());

View File

@@ -46,7 +46,6 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
u32 BytesPerPixel(TextureFormat format) {
switch (format) {
case TextureFormat::DXT1:
case TextureFormat::DXN1:
// In this case a 'pixel' actually refers to a 4x4 tile.
return 8;
case TextureFormat::DXT23:
@@ -55,15 +54,10 @@ u32 BytesPerPixel(TextureFormat format) {
return 16;
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::BF10GF11RF11:
return 4;
case TextureFormat::A1B5G5R5:
case TextureFormat::B5G6R5:
return 2;
case TextureFormat::R8:
return 1;
case TextureFormat::R16_G16_B16_A16:
return 8;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
@@ -81,9 +75,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::DXT1:
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
// In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
// values.
// In the DXT formats, each 4x4 tile is swizzled instead of just individual pixel values.
CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
break;
@@ -91,9 +83,6 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::A2B10G10R10:
case TextureFormat::A1B5G5R5:
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;
@@ -114,13 +103,10 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::DXT1:
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
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;

View File

@@ -122,17 +122,6 @@ 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;
@@ -150,11 +139,6 @@ 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 {

View File

@@ -335,24 +335,6 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
}
}
bool GMainWindow::SupportsRequiredGLExtensions() {
QStringList unsupported_ext;
if (!GLAD_GL_ARB_program_interface_query)
unsupported_ext.append("ARB_program_interface_query");
if (!GLAD_GL_ARB_separate_shader_objects)
unsupported_ext.append("ARB_separate_shader_objects");
if (!GLAD_GL_ARB_shader_storage_buffer_object)
unsupported_ext.append("ARB_shader_storage_buffer_object");
if (!GLAD_GL_ARB_vertex_attrib_binding)
unsupported_ext.append("ARB_vertex_attrib_binding");
for (const QString& ext : unsupported_ext)
NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
return unsupported_ext.empty();
}
bool GMainWindow::LoadROM(const QString& filename) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
@@ -368,14 +350,6 @@ bool GMainWindow::LoadROM(const QString& filename) {
return false;
}
if (!SupportsRequiredGLExtensions()) {
QMessageBox::critical(
this, tr("Error while initializing OpenGL Core!"),
tr("Your GPU may not support one or more required OpenGL extensions. Please "
"ensure you have the latest graphics driver. See the log for more details."));
return false;
}
Core::System& system{Core::System::GetInstance()};
system.SetGPUDebugContext(debug_context);

View File

@@ -79,7 +79,6 @@ private:
void ConnectWidgetEvents();
void ConnectMenuEvents();
bool SupportsRequiredGLExtensions();
bool LoadROM(const QString& filename);
void BootGame(const QString& filename);
void ShutdownGame();

View File

@@ -78,24 +78,6 @@ void EmuWindow_SDL2::Fullscreen() {
SDL_MaximizeWindow(render_window);
}
bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
std::vector<std::string> unsupported_ext;
if (!GLAD_GL_ARB_program_interface_query)
unsupported_ext.push_back("ARB_program_interface_query");
if (!GLAD_GL_ARB_separate_shader_objects)
unsupported_ext.push_back("ARB_separate_shader_objects");
if (!GLAD_GL_ARB_shader_storage_buffer_object)
unsupported_ext.push_back("ARB_shader_storage_buffer_object");
if (!GLAD_GL_ARB_vertex_attrib_binding)
unsupported_ext.push_back("ARB_vertex_attrib_binding");
for (const std::string& ext : unsupported_ext)
NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
return unsupported_ext.empty();
}
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
InputCommon::Init();
@@ -146,11 +128,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
exit(1);
}
if (!SupportsRequiredGLExtensions()) {
NGLOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
exit(1);
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();

View File

@@ -46,9 +46,6 @@ private:
/// Called when user passes the fullscreen parameter flag
void Fullscreen();
/// Whether the GPU and driver supports the OpenGL extension required
bool SupportsRequiredGLExtensions();
/// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;