Compare commits
6 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3f22ebc18 | ||
|
|
8995ad6f29 | ||
|
|
241d3c1473 | ||
|
|
b4043ea231 | ||
|
|
0f7b1a2818 | ||
|
|
50115fefa6 |
@@ -42,7 +42,3 @@ notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://api.yuzu-emu.org/code/travis/notify
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ccache
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget cmake ninja-build ccache
|
||||
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
|
||||
sh cmake-3.10.1-Linux-x86_64.sh --exclude-subdir --prefix=/ --skip-license
|
||||
|
||||
cd /yuzu
|
||||
|
||||
export PATH=/usr/lib/ccache:$PATH
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
||||
mkdir build && cd build
|
||||
ccache --show-stats > ccache_before
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
ninja
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -7,12 +7,8 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
|
||||
mkdir build && cd build
|
||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
||||
ccache --show-stats > ccache_before
|
||||
cmake --version
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
brew update
|
||||
brew install dylibbundler p7zip qt5 sdl2 ccache
|
||||
brew install dylibbundler p7zip qt5 sdl2
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
|
||||
@@ -41,7 +41,6 @@ namespace Log {
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NS) \
|
||||
|
||||
@@ -61,7 +61,6 @@ enum class Class : ClassType {
|
||||
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
|
||||
|
||||
@@ -64,10 +64,6 @@ std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
|
||||
// Turns " hej " into "hej". Also handles tabs.
|
||||
std::string StripSpaces(const std::string& str) {
|
||||
const size_t s = str.find_first_not_of(" \t\r\n");
|
||||
|
||||
@@ -21,8 +21,6 @@ std::string ToUpper(std::string str);
|
||||
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
std::string StripQuotes(const std::string& s);
|
||||
|
||||
|
||||
@@ -148,8 +148,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -316,11 +316,6 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
NGLOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
@@ -259,7 +258,9 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
u64 mode = rp.Pop<u64>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
@@ -274,7 +275,9 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
@@ -286,7 +289,9 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
|
||||
@@ -300,11 +305,13 @@ public:
|
||||
std::vector<u8> buffer;
|
||||
buffer.resize(ctx.BufferDescriptorX()[0].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
|
||||
std::string src_name = Common::StringFromBuffer(buffer);
|
||||
auto end = std::find(buffer.begin(), buffer.end(), '\0');
|
||||
std::string src_name(buffer.begin(), end);
|
||||
|
||||
buffer.resize(ctx.BufferDescriptorX()[1].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
|
||||
std::string dst_name = Common::StringFromBuffer(buffer);
|
||||
end = std::find(buffer.begin(), buffer.end(), '\0');
|
||||
std::string dst_name(buffer.begin(), end);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
@@ -316,7 +323,9 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
|
||||
|
||||
@@ -340,7 +349,9 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
// TODO(Subv): Implement this filter.
|
||||
u32 filter_flags = rp.Pop<u32>();
|
||||
@@ -365,7 +376,9 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ private:
|
||||
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
|
||||
|
||||
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
|
||||
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
|
||||
entry.timestamp++;
|
||||
// TODO(shinyquagsire23): Is this always identical to timestamp?
|
||||
entry.timestamp_2++;
|
||||
@@ -102,8 +103,6 @@ private:
|
||||
if (controller != Controller_Handheld)
|
||||
continue;
|
||||
|
||||
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
|
||||
|
||||
// TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
|
||||
// For now everything is just the default handheld layout, but split Joy-Con will
|
||||
// rotate the face buttons and directions for certain layouts.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::HID {
|
||||
// Begin enums and output structs
|
||||
|
||||
constexpr u32 HID_NUM_ENTRIES = 17;
|
||||
constexpr u32 HID_NUM_LAYOUTS = 7;
|
||||
constexpr u32 HID_NUM_LAYOUTS = 2;
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x8000;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x8000;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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(¶ms, input.data(), input.size());
|
||||
// TODO : To implement properly
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
IoctlFlushL2 params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
// TODO : To implement properly
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#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"
|
||||
@@ -192,7 +191,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
Friend::InstallInterfaces(*sm);
|
||||
HID::InstallInterfaces(*sm);
|
||||
LM::InstallInterfaces(*sm);
|
||||
MM::InstallInterfaces(*sm);
|
||||
NFP::InstallInterfaces(*sm);
|
||||
NIFM::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm);
|
||||
|
||||
@@ -9,8 +9,6 @@ add_library(video_core STATIC
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
engines/maxwell_dma.cpp
|
||||
engines/maxwell_dma.h
|
||||
engines/shader_bytecode.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
@@ -41,8 +39,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/maxwell_to_gl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
textures/astc.cpp
|
||||
textures/astc.h
|
||||
textures/decoders.cpp
|
||||
textures/decoders.h
|
||||
textures/texture.h
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -61,11 +60,8 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
maxwell_dma->WriteReg(method, value);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
|
||||
if (regs.src.linear == regs.dst.linear) {
|
||||
// If the input layout and the output layout are the same, just perform a raw copy.
|
||||
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
|
||||
Memory::CopyBlock(dest_cpu, source_cpu,
|
||||
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
|
||||
return;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -318,7 +318,6 @@ public:
|
||||
Equation equation_a;
|
||||
Factor factor_source_a;
|
||||
Factor factor_dest_a;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
};
|
||||
|
||||
union {
|
||||
@@ -355,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 {
|
||||
@@ -397,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);
|
||||
@@ -433,27 +416,7 @@ public:
|
||||
};
|
||||
} rt_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0x31);
|
||||
|
||||
u32 independent_blend_enable;
|
||||
|
||||
INSERT_PADDING_WORDS(0x15);
|
||||
|
||||
struct {
|
||||
u32 separate_alpha;
|
||||
Blend::Equation equation_rgb;
|
||||
Blend::Factor factor_source_rgb;
|
||||
Blend::Factor factor_dest_rgb;
|
||||
Blend::Equation equation_a;
|
||||
Blend::Factor factor_source_a;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
Blend::Factor factor_dest_a;
|
||||
|
||||
u32 enable_common;
|
||||
u32 enable[NumRenderTargets];
|
||||
} blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0x77);
|
||||
INSERT_PADDING_WORDS(0xCF);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
@@ -578,7 +541,9 @@ public:
|
||||
|
||||
} vertex_array[NumVertexArrays];
|
||||
|
||||
Blend independent_blend[NumRenderTargets];
|
||||
Blend blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0x39);
|
||||
|
||||
struct {
|
||||
u32 limit_high;
|
||||
@@ -683,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;
|
||||
|
||||
@@ -741,8 +703,6 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
|
||||
ASSERT_REG_POSITION(blend, 0x4CF);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
@@ -750,7 +710,7 @@ ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(independent_blend, 0x780);
|
||||
ASSERT_REG_POSITION(blend, 0x780);
|
||||
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
|
||||
void MaxwellDMA::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellDMA register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
#define MAXWELLDMA_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELLDMA_REG_INDEX(exec): {
|
||||
HandleCopy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef MAXWELLDMA_REG_INDEX
|
||||
}
|
||||
|
||||
void MaxwellDMA::HandleCopy() {
|
||||
NGLOG_WARNING(HW_GPU, "Requested a DMA copy");
|
||||
|
||||
const GPUVAddr source = regs.src_address.Address();
|
||||
const GPUVAddr dest = regs.dst_address.Address();
|
||||
|
||||
const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
|
||||
const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
ASSERT(regs.exec.enable_swizzle == 0);
|
||||
ASSERT(regs.exec.enable_2d == 1);
|
||||
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
|
||||
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
|
||||
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
|
||||
ASSERT(regs.src_params.pos_x == 0);
|
||||
ASSERT(regs.src_params.pos_y == 0);
|
||||
ASSERT(regs.dst_params.pos_x == 0);
|
||||
ASSERT(regs.dst_params.pos_y == 0);
|
||||
ASSERT(regs.exec.is_dst_linear != regs.exec.is_src_linear);
|
||||
|
||||
u8* src_buffer = Memory::GetPointer(source_cpu);
|
||||
u8* dst_buffer = Memory::GetPointer(dest_cpu);
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
|
||||
dst_buffer, true, regs.src_params.BlockHeight());
|
||||
} else {
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
|
||||
src_buffer, false, regs.dst_params.BlockHeight());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
@@ -1,155 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
class MaxwellDMA final {
|
||||
public:
|
||||
explicit MaxwellDMA(MemoryManager& memory_manager);
|
||||
~MaxwellDMA() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value);
|
||||
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x1D6;
|
||||
|
||||
struct Parameters {
|
||||
union {
|
||||
BitField<0, 4, u32> block_depth;
|
||||
BitField<4, 4, u32> block_height;
|
||||
BitField<8, 4, u32> block_width;
|
||||
};
|
||||
u32 size_x;
|
||||
u32 size_y;
|
||||
u32 size_z;
|
||||
u32 pos_z;
|
||||
union {
|
||||
BitField<0, 16, u32> pos_x;
|
||||
BitField<16, 16, u32> pos_y;
|
||||
};
|
||||
|
||||
u32 BlockHeight() const {
|
||||
return 1 << block_height;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
|
||||
|
||||
enum class CopyMode : u32 {
|
||||
None = 0,
|
||||
Unk1 = 1,
|
||||
Unk2 = 2,
|
||||
};
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
None = 0,
|
||||
Short = 1,
|
||||
Long = 2,
|
||||
};
|
||||
|
||||
enum class QueryIntr : u32 {
|
||||
None = 0,
|
||||
Block = 1,
|
||||
NonBlock = 2,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0xC0);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 2, CopyMode> copy_mode;
|
||||
BitField<2, 1, u32> flush;
|
||||
|
||||
BitField<3, 2, QueryMode> query_mode;
|
||||
BitField<5, 2, QueryIntr> query_intr;
|
||||
|
||||
BitField<7, 1, u32> is_src_linear;
|
||||
BitField<8, 1, u32> is_dst_linear;
|
||||
|
||||
BitField<9, 1, u32> enable_2d;
|
||||
BitField<10, 1, u32> enable_swizzle;
|
||||
};
|
||||
} exec;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3F);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} src_address;
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} dst_address;
|
||||
|
||||
u32 src_pitch;
|
||||
u32 dst_pitch;
|
||||
u32 x_count;
|
||||
u32 y_count;
|
||||
|
||||
INSERT_PADDING_WORDS(0xBB);
|
||||
|
||||
Parameters dst_params;
|
||||
|
||||
INSERT_PADDING_WORDS(1);
|
||||
|
||||
Parameters src_params;
|
||||
|
||||
INSERT_PADDING_WORDS(0x13);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void HandleCopy();
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(exec, 0xC0);
|
||||
ASSERT_REG_POSITION(src_address, 0x100);
|
||||
ASSERT_REG_POSITION(dst_address, 0x102);
|
||||
ASSERT_REG_POSITION(src_pitch, 0x104);
|
||||
ASSERT_REG_POSITION(dst_pitch, 0x105);
|
||||
ASSERT_REG_POSITION(x_count, 0x106);
|
||||
ASSERT_REG_POSITION(y_count, 0x107);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
@@ -109,6 +109,11 @@ union Sampler {
|
||||
u64 value{};
|
||||
};
|
||||
|
||||
union Uniform {
|
||||
BitField<20, 14, u64> offset;
|
||||
BitField<34, 5, u64> index;
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -151,13 +156,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 +166,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;
|
||||
@@ -216,12 +189,12 @@ union Instruction {
|
||||
|
||||
union {
|
||||
BitField<20, 19, u64> imm20_19;
|
||||
BitField<20, 32, s64> imm20_32;
|
||||
BitField<20, 32, u64> imm20_32;
|
||||
BitField<45, 1, u64> negate_b;
|
||||
BitField<46, 1, u64> abs_a;
|
||||
BitField<48, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_b;
|
||||
BitField<50, 1, u64> saturate_d;
|
||||
BitField<50, 1, u64> abs_d;
|
||||
BitField<56, 1, u64> negate_imm;
|
||||
|
||||
union {
|
||||
@@ -229,20 +202,6 @@ union Instruction {
|
||||
BitField<42, 1, u64> negate_pred;
|
||||
} fmnmx;
|
||||
|
||||
union {
|
||||
BitField<39, 1, u64> invert_a;
|
||||
BitField<40, 1, u64> invert_b;
|
||||
BitField<41, 2, LogicOperation> operation;
|
||||
BitField<44, 2, u64> unk44;
|
||||
BitField<48, 3, Pred> pred48;
|
||||
} lop;
|
||||
|
||||
union {
|
||||
BitField<53, 2, LogicOperation> operation;
|
||||
BitField<55, 1, u64> invert_a;
|
||||
BitField<56, 1, u64> invert_b;
|
||||
} lop32i;
|
||||
|
||||
float GetImm20_19() const {
|
||||
float result{};
|
||||
u32 imm{static_cast<u32>(imm20_19)};
|
||||
@@ -254,55 +213,17 @@ union Instruction {
|
||||
|
||||
float GetImm20_32() const {
|
||||
float result{};
|
||||
s32 imm{static_cast<s32>(imm20_32)};
|
||||
u32 imm{static_cast<u32>(imm20_32)};
|
||||
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<48, 1, u64> is_signed;
|
||||
} shift;
|
||||
|
||||
union {
|
||||
BitField<39, 5, u64> shift_amount;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_a;
|
||||
} alu_integer;
|
||||
|
||||
union {
|
||||
BitField<54, 1, u64> saturate;
|
||||
BitField<56, 1, u64> negate_a;
|
||||
} iadd32i;
|
||||
|
||||
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;
|
||||
@@ -317,16 +238,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;
|
||||
@@ -334,38 +245,20 @@ 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<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
BitField<44, 1, u64> bf;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
BitField<49, 3, PredCondition> cond;
|
||||
} iset;
|
||||
|
||||
union {
|
||||
BitField<8, 2, Register::Size> dest_size;
|
||||
BitField<10, 2, Register::Size> src_size;
|
||||
BitField<12, 1, u64> is_output_signed;
|
||||
BitField<13, 1, u64> is_input_signed;
|
||||
BitField<10, 2, Register::Size> size;
|
||||
BitField<13, 1, u64> is_signed;
|
||||
BitField<41, 2, u64> selector;
|
||||
BitField<45, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_a;
|
||||
|
||||
union {
|
||||
BitField<39, 2, F2iRoundingOp> rounding;
|
||||
} f2i;
|
||||
|
||||
union {
|
||||
BitField<39, 4, F2fRoundingOp> rounding;
|
||||
} f2f;
|
||||
BitField<50, 1, u64> saturate_a;
|
||||
} conversion;
|
||||
|
||||
union {
|
||||
@@ -377,53 +270,16 @@ union Instruction {
|
||||
} 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;
|
||||
}
|
||||
// TODO(bunnei): This is just a guess, needs to be verified
|
||||
BitField<52, 1, u64> enable_g_component;
|
||||
} 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;
|
||||
@@ -436,13 +292,7 @@ class OpCode {
|
||||
public:
|
||||
enum class Id {
|
||||
KIL,
|
||||
SSY,
|
||||
BFE_C,
|
||||
BFE_R,
|
||||
BFE_IMM,
|
||||
BRA,
|
||||
LD_A,
|
||||
LD_C,
|
||||
ST_A,
|
||||
TEX,
|
||||
TEXQ, // Texture Query
|
||||
@@ -461,13 +311,6 @@ public:
|
||||
FMUL_R,
|
||||
FMUL_IMM,
|
||||
FMUL32_IMM,
|
||||
IADD_C,
|
||||
IADD_R,
|
||||
IADD_IMM,
|
||||
IADD32I,
|
||||
ISCADD_C, // Scale and Add
|
||||
ISCADD_R,
|
||||
ISCADD_IMM,
|
||||
MUFU, // Multi-Function Operator
|
||||
RRO_C, // Range Reduction Operator
|
||||
RRO_R,
|
||||
@@ -484,26 +327,17 @@ public:
|
||||
I2I_C,
|
||||
I2I_R,
|
||||
I2I_IMM,
|
||||
LOP_C,
|
||||
LOP_R,
|
||||
LOP_IMM,
|
||||
LOP32I,
|
||||
MOV_C,
|
||||
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,
|
||||
@@ -513,29 +347,17 @@ public:
|
||||
ISETP_C,
|
||||
ISETP_IMM,
|
||||
ISETP_R,
|
||||
ISET_R,
|
||||
ISET_C,
|
||||
ISET_IMM,
|
||||
PSETP,
|
||||
XMAD_IMM,
|
||||
XMAD_CR,
|
||||
XMAD_RC,
|
||||
XMAD_RR,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
Trivial,
|
||||
Arithmetic,
|
||||
ArithmeticInteger,
|
||||
ArithmeticIntegerImmediate,
|
||||
Bfe,
|
||||
Shift,
|
||||
Ffma,
|
||||
Flow,
|
||||
Memory,
|
||||
FloatSet,
|
||||
FloatSetPredicate,
|
||||
IntegerSet,
|
||||
IntegerSetPredicate,
|
||||
PredicateSetPredicate,
|
||||
Conversion,
|
||||
@@ -634,10 +456,7 @@ 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("111000101001----", Id::SSY, Type::Flow, "SSY"),
|
||||
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"),
|
||||
@@ -656,13 +475,6 @@ 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("0100110000010---", Id::IADD_C, Type::ArithmeticInteger, "IADD_C"),
|
||||
INST("0101110000010---", Id::IADD_R, Type::ArithmeticInteger, "IADD_R"),
|
||||
INST("0011100-00010---", Id::IADD_IMM, Type::ArithmeticInteger, "IADD_IMM"),
|
||||
INST("0001110---------", Id::IADD32I, Type::ArithmeticIntegerImmediate, "IADD32I"),
|
||||
INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"),
|
||||
INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"),
|
||||
INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "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"),
|
||||
@@ -670,32 +482,20 @@ private:
|
||||
INST("0100110010101---", Id::F2F_C, Type::Conversion, "F2F_C"),
|
||||
INST("0101110010101---", Id::F2F_R, Type::Conversion, "F2F_R"),
|
||||
INST("0011100-10101---", Id::F2F_IMM, Type::Conversion, "F2F_IMM"),
|
||||
INST("0100110010110---", Id::F2I_C, Type::Conversion, "F2I_C"),
|
||||
INST("0101110010110---", Id::F2I_R, Type::Conversion, "F2I_R"),
|
||||
INST("0011100-10110---", Id::F2I_IMM, Type::Conversion, "F2I_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("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
|
||||
INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
|
||||
INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
|
||||
INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "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"),
|
||||
@@ -711,14 +511,7 @@ private:
|
||||
INST("010010110110----", Id::ISETP_C, Type::IntegerSetPredicate, "ISETP_C"),
|
||||
INST("010110110110----", Id::ISETP_R, Type::IntegerSetPredicate, "ISETP_R"),
|
||||
INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerSetPredicate, "ISETP_IMM"),
|
||||
INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"),
|
||||
INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"),
|
||||
INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_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) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -15,7 +14,6 @@ GPU::GPU() {
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
@@ -28,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;
|
||||
|
||||
@@ -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.
|
||||
@@ -63,7 +61,6 @@ namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
class MaxwellDMA;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
@@ -104,8 +101,6 @@ private:
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
/// DMA engine
|
||||
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -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,17 +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));
|
||||
|
||||
// Flush the buffer so that the GPU can see the data we just wrote.
|
||||
glFlushMappedBufferRange(GL_ARRAY_BUFFER, buffer_offset, 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);
|
||||
@@ -264,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();
|
||||
@@ -309,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,12 +338,12 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
// Sync the viewport
|
||||
SyncViewport(surfaces_rect, res_scale);
|
||||
|
||||
// Sync the blend state registers
|
||||
SyncBlendState();
|
||||
|
||||
// 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;
|
||||
@@ -458,7 +447,65 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {}
|
||||
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) {
|
||||
case MAXWELL3D_REG_INDEX(blend.separate_alpha):
|
||||
ASSERT_MSG(false, "unimplemented");
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.equation_rgb):
|
||||
state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_source_rgb):
|
||||
state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_dest_rgb):
|
||||
state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.equation_a):
|
||||
state.blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_source_a):
|
||||
state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_dest_a):
|
||||
state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAll() {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
@@ -607,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);
|
||||
@@ -636,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;
|
||||
@@ -710,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;
|
||||
@@ -738,21 +730,14 @@ void RasterizerOpenGL::SyncDepthOffset() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendState() {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(Subv): Support more than just render target 0.
|
||||
state.blend.enabled = regs.blend.enable[0] != 0;
|
||||
|
||||
if (!state.blend.enabled)
|
||||
return;
|
||||
|
||||
ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented");
|
||||
ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented");
|
||||
state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb);
|
||||
state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb);
|
||||
state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_rgb);
|
||||
state.blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_a);
|
||||
state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_a);
|
||||
state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_a);
|
||||
void RasterizerOpenGL::SyncBlendEnabled() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendFuncs() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendColor() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -121,8 +113,14 @@ private:
|
||||
/// Syncs the depth offset to match the guest state
|
||||
void SyncDepthOffset();
|
||||
|
||||
/// Syncs the blend state to match the guest state
|
||||
void SyncBlendState();
|
||||
/// Syncs the blend enabled status to match the guest state
|
||||
void SyncBlendEnabled();
|
||||
|
||||
/// Syncs the blend functions to match the guest state
|
||||
void SyncBlendFuncs();
|
||||
|
||||
/// Syncs the blend color to match the guest state
|
||||
void SyncBlendColor();
|
||||
|
||||
bool has_ARB_buffer_storage;
|
||||
bool has_ARB_direct_state_access;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/textures/astc.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
#include "video_core/utils.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -51,22 +50,18 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5
|
||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F
|
||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F
|
||||
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45
|
||||
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4
|
||||
}};
|
||||
|
||||
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, or RGBA16F which is type FLOAT
|
||||
ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F);
|
||||
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||
// TODO(Subv): Implement depth formats
|
||||
@@ -88,23 +83,6 @@ static u16 GetResolutionScaleFactor() {
|
||||
: Settings::values.resolution_factor);
|
||||
}
|
||||
|
||||
static void ConvertASTCToRGBA8(std::vector<u8>& data, PixelFormat format, u32 width, u32 height) {
|
||||
u32 block_width{};
|
||||
u32 block_height{};
|
||||
|
||||
switch (format) {
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
block_width = 4;
|
||||
block_height = 4;
|
||||
break;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height);
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr base,
|
||||
Tegra::GPUVAddr start, Tegra::GPUVAddr end) {
|
||||
@@ -116,12 +94,6 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::
|
||||
auto data = Tegra::Texture::UnswizzleTexture(
|
||||
*gpu.memory_manager->GpuToCpuAddress(base),
|
||||
SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
|
||||
|
||||
if (SurfaceParams::IsFormatASTC(format)) {
|
||||
// ASTC formats are converted to RGBA8 in software, as most PC GPUs do not support this
|
||||
ConvertASTCToRGBA8(data, format, stride, height);
|
||||
}
|
||||
|
||||
std::memcpy(gl_buffer, data.data(), data.size());
|
||||
} else {
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check
|
||||
@@ -138,12 +110,11 @@ 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::ASTC_2D_4X4>,
|
||||
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
|
||||
MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>,
|
||||
MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>,
|
||||
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
|
||||
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
|
||||
};
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr,
|
||||
@@ -156,13 +127,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
|
||||
MortonCopy<false, PixelFormat::A1B5G5R5>,
|
||||
MortonCopy<false, PixelFormat::R8>,
|
||||
MortonCopy<false, PixelFormat::RGBA16F>,
|
||||
MortonCopy<false, PixelFormat::R11FG11FB10F>,
|
||||
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
};
|
||||
|
||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||
@@ -195,10 +164,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;
|
||||
}
|
||||
|
||||
@@ -914,6 +933,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
|
||||
// Use GetSurfaceSubRect instead
|
||||
ASSERT(params.width == params.stride);
|
||||
|
||||
ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0));
|
||||
|
||||
// Check for an exact match in existing surfaces
|
||||
Surface surface =
|
||||
FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
|
||||
@@ -1056,11 +1077,8 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
params.addr = config.tic.Address();
|
||||
params.is_tiled = config.tic.IsTiled();
|
||||
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
|
||||
|
||||
params.width = Common::AlignUp(config.tic.Width(), params.GetCompresssionFactor()) /
|
||||
params.GetCompresssionFactor();
|
||||
params.height = Common::AlignUp(config.tic.Height(), params.GetCompresssionFactor()) /
|
||||
params.GetCompresssionFactor();
|
||||
params.width = config.tic.Width() / params.GetCompresssionFactor();
|
||||
params.height = config.tic.Height() / params.GetCompresssionFactor();
|
||||
|
||||
// TODO(Subv): Different types per component are not supported.
|
||||
ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() &&
|
||||
@@ -1071,13 +1089,6 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
|
||||
if (config.tic.IsTiled()) {
|
||||
params.block_height = config.tic.BlockHeight();
|
||||
|
||||
// TODO(bunnei): The below align up is a hack. This is here because some compressed textures
|
||||
// are not a multiple of their own compression factor, and so this accounts for that. This
|
||||
// could potentially result in an extra row of 4px being decoded if a texture is not a
|
||||
// multiple of 4.
|
||||
params.width = Common::AlignUp(params.width, 4);
|
||||
params.height = Common::AlignUp(params.height, 4);
|
||||
} else {
|
||||
// Use the texture-provided stride value if the texture isn't tiled.
|
||||
params.stride = static_cast<u32>(params.PixelsInBytes(config.tic.Pitch()));
|
||||
@@ -1085,6 +1096,23 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
|
||||
params.UpdateParams();
|
||||
|
||||
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);
|
||||
|
||||
params.res_scale = src_surface->res_scale;
|
||||
Surface tmp_surface = CreateSurface(params);
|
||||
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
|
||||
tmp_surface->GetScaledRect(),
|
||||
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
|
||||
draw_framebuffer.handle);
|
||||
|
||||
remove_surfaces.emplace(tmp_surface);
|
||||
return tmp_surface;
|
||||
}
|
||||
|
||||
return GetSurface(params, ScaleMatch::Ignore, true);
|
||||
}
|
||||
|
||||
@@ -1259,7 +1287,7 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, Tegra::GPUVA
|
||||
|
||||
const auto interval = *it & validate_interval;
|
||||
// Look for a valid surface to copy from
|
||||
SurfaceParams params = *surface;
|
||||
SurfaceParams params = surface->FromInterval(interval);
|
||||
|
||||
Surface copy_surface =
|
||||
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
|
||||
|
||||
@@ -60,12 +60,10 @@ struct SurfaceParams {
|
||||
A1B5G5R5 = 3,
|
||||
R8 = 4,
|
||||
RGBA16F = 5,
|
||||
R11FG11FB10F = 6,
|
||||
DXT1 = 7,
|
||||
DXT23 = 8,
|
||||
DXT45 = 9,
|
||||
DXN1 = 10, // This is also known as BC4
|
||||
ASTC_2D_4X4 = 11,
|
||||
DXT1 = 6,
|
||||
DXT23 = 7,
|
||||
DXT45 = 8,
|
||||
DXN1 = 9, // This is also known as BC4
|
||||
|
||||
Max,
|
||||
Invalid = 255,
|
||||
@@ -106,13 +104,11 @@ struct SurfaceParams {
|
||||
1, // A2B10G10R10
|
||||
1, // A1B5G5R5
|
||||
1, // R8
|
||||
1, // RGBA16F
|
||||
1, // R11FG11FB10F
|
||||
2, // RGBA16F
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
1, // ASTC_2D_4X4
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
|
||||
@@ -133,12 +129,10 @@ struct SurfaceParams {
|
||||
16, // A1B5G5R5
|
||||
8, // R8
|
||||
64, // RGBA16F
|
||||
32, // R11FG11FB10F
|
||||
64, // DXT1
|
||||
128, // DXT23
|
||||
128, // DXT45
|
||||
64, // DXN1
|
||||
32, // ASTC_2D_4X4
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<size_t>(format) < bpp_table.size());
|
||||
@@ -157,23 +151,12 @@ struct SurfaceParams {
|
||||
return PixelFormat::A2B10G10R10;
|
||||
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return PixelFormat::R11FG11FB10F;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsFormatASTC(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
@@ -199,8 +182,6 @@ struct SurfaceParams {
|
||||
return PixelFormat::R8;
|
||||
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::Texture::TextureFormat::BF10GF11RF11:
|
||||
return PixelFormat::R11FG11FB10F;
|
||||
case Tegra::Texture::TextureFormat::DXT1:
|
||||
return PixelFormat::DXT1;
|
||||
case Tegra::Texture::TextureFormat::DXT23:
|
||||
@@ -209,8 +190,6 @@ struct SurfaceParams {
|
||||
return PixelFormat::DXT45;
|
||||
case Tegra::Texture::TextureFormat::DXN1:
|
||||
return PixelFormat::DXN1;
|
||||
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
|
||||
return PixelFormat::ASTC_2D_4X4;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
@@ -232,8 +211,6 @@ struct SurfaceParams {
|
||||
return Tegra::Texture::TextureFormat::R8;
|
||||
case PixelFormat::RGBA16F:
|
||||
return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
|
||||
case PixelFormat::R11FG11FB10F:
|
||||
return Tegra::Texture::TextureFormat::BF10GF11RF11;
|
||||
case PixelFormat::DXT1:
|
||||
return Tegra::Texture::TextureFormat::DXT1;
|
||||
case PixelFormat::DXT23:
|
||||
@@ -242,8 +219,6 @@ struct SurfaceParams {
|
||||
return Tegra::Texture::TextureFormat::DXT45;
|
||||
case PixelFormat::DXN1:
|
||||
return Tegra::Texture::TextureFormat::DXN1;
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -268,7 +243,6 @@ struct SurfaceParams {
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return ComponentType::UNorm;
|
||||
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
|
||||
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return ComponentType::Float;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,10 +39,6 @@ void main() {
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
gl_Position = position;
|
||||
|
||||
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
|
||||
// For now, this is here to bring order in lieu of proper emulation
|
||||
position.w = 1.0;
|
||||
}
|
||||
)";
|
||||
out += program.first;
|
||||
@@ -66,6 +62,8 @@ layout (std140) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
};
|
||||
|
||||
uniform sampler2D tex[32];
|
||||
|
||||
void main() {
|
||||
exec_shader();
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace Tegra::Texture::ASTC {
|
||||
|
||||
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
|
||||
uint32_t block_width, uint32_t block_height);
|
||||
|
||||
} // namespace Tegra::Texture::ASTC
|
||||
@@ -53,10 +53,8 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
case TextureFormat::DXT45:
|
||||
// In this case a 'pixel' actually refers to a 4x4 tile.
|
||||
return 16;
|
||||
case TextureFormat::ASTC_2D_4X4:
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::A2B10G10R10:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
return 4;
|
||||
case TextureFormat::A1B5G5R5:
|
||||
case TextureFormat::B5G6R5:
|
||||
@@ -94,8 +92,6 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
|
||||
case TextureFormat::B5G6R5:
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::R16_G16_B16_A16:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
case TextureFormat::ASTC_2D_4X4:
|
||||
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
|
||||
unswizzled_data.data(), true, block_height);
|
||||
break;
|
||||
@@ -117,13 +113,11 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
case TextureFormat::DXT23:
|
||||
case TextureFormat::DXT45:
|
||||
case TextureFormat::DXN1:
|
||||
case TextureFormat::ASTC_2D_4X4:
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -32,6 +32,8 @@ add_executable(yuzu
|
||||
debugger/graphics/graphics_surface.h
|
||||
debugger/profiler.cpp
|
||||
debugger/profiler.h
|
||||
debugger/registers.cpp
|
||||
debugger/registers.h
|
||||
debugger/wait_tree.cpp
|
||||
debugger/wait_tree.h
|
||||
game_list.cpp
|
||||
@@ -58,6 +60,7 @@ set(UIS
|
||||
configuration/configure_graphics.ui
|
||||
configuration/configure_input.ui
|
||||
configuration/configure_system.ui
|
||||
debugger/registers.ui
|
||||
hotkeys.ui
|
||||
main.ui
|
||||
)
|
||||
|
||||
190
src/yuzu/debugger/registers.cpp
Normal file
190
src/yuzu/debugger/registers.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "yuzu/debugger/registers.h"
|
||||
#include "yuzu/util/util.h"
|
||||
|
||||
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
|
||||
cpu_regs_ui.setupUi(this);
|
||||
|
||||
tree = cpu_regs_ui.treeWidget;
|
||||
tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers"))));
|
||||
tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers"))));
|
||||
tree->addTopLevelItem(vfp_system_registers =
|
||||
new QTreeWidgetItem(QStringList(tr("VFP System Registers"))));
|
||||
tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR")));
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i)));
|
||||
core_registers->addChild(child);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("S[%1]").arg(i)));
|
||||
vfp_registers->addChild(child);
|
||||
}
|
||||
|
||||
QFont font = GetMonospaceFont();
|
||||
|
||||
CreateCPSRChildren();
|
||||
CreateVFPSystemRegisterChildren();
|
||||
|
||||
// Set Registers to display in monospace font
|
||||
for (int i = 0; i < core_registers->childCount(); ++i)
|
||||
core_registers->child(i)->setFont(1, font);
|
||||
|
||||
for (int i = 0; i < vfp_registers->childCount(); ++i)
|
||||
vfp_registers->child(i)->setFont(1, font);
|
||||
|
||||
for (int i = 0; i < vfp_system_registers->childCount(); ++i) {
|
||||
vfp_system_registers->child(i)->setFont(1, font);
|
||||
for (int x = 0; x < vfp_system_registers->child(i)->childCount(); ++x) {
|
||||
vfp_system_registers->child(i)->child(x)->setFont(1, font);
|
||||
}
|
||||
}
|
||||
// Set CSPR to display in monospace font
|
||||
cpsr->setFont(1, font);
|
||||
for (int i = 0; i < cpsr->childCount(); ++i) {
|
||||
cpsr->child(i)->setFont(1, font);
|
||||
for (int x = 0; x < cpsr->child(i)->childCount(); ++x) {
|
||||
cpsr->child(i)->child(x)->setFont(1, font);
|
||||
}
|
||||
}
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void RegistersWidget::OnDebugModeEntered() {
|
||||
if (!Core::System::GetInstance().IsPoweredOn())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < core_registers->childCount(); ++i)
|
||||
core_registers->child(i)->setText(
|
||||
1, QString("0x%1").arg(Core::CurrentArmInterface().GetReg(i), 8, 16, QLatin1Char('0')));
|
||||
|
||||
UpdateCPSRValues();
|
||||
}
|
||||
|
||||
void RegistersWidget::OnDebugModeLeft() {}
|
||||
|
||||
void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void RegistersWidget::OnEmulationStopping() {
|
||||
// Reset widget text
|
||||
for (int i = 0; i < core_registers->childCount(); ++i)
|
||||
core_registers->child(i)->setText(1, QString(""));
|
||||
|
||||
for (int i = 0; i < vfp_registers->childCount(); ++i)
|
||||
vfp_registers->child(i)->setText(1, QString(""));
|
||||
|
||||
for (int i = 0; i < cpsr->childCount(); ++i)
|
||||
cpsr->child(i)->setText(1, QString(""));
|
||||
|
||||
cpsr->setText(1, QString(""));
|
||||
|
||||
// FPSCR
|
||||
for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i)
|
||||
vfp_system_registers->child(0)->child(i)->setText(1, QString(""));
|
||||
|
||||
// FPEXC
|
||||
for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i)
|
||||
vfp_system_registers->child(1)->child(i)->setText(1, QString(""));
|
||||
|
||||
vfp_system_registers->child(0)->setText(1, QString(""));
|
||||
vfp_system_registers->child(1)->setText(1, QString(""));
|
||||
vfp_system_registers->child(2)->setText(1, QString(""));
|
||||
vfp_system_registers->child(3)->setText(1, QString(""));
|
||||
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void RegistersWidget::CreateCPSRChildren() {
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("M")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("T")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("F")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("I")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("A")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("E")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("IT")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("GE")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("DNM")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("J")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("Q")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("V")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("C")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("Z")));
|
||||
cpsr->addChild(new QTreeWidgetItem(QStringList("N")));
|
||||
}
|
||||
|
||||
void RegistersWidget::UpdateCPSRValues() {
|
||||
const u32 cpsr_val = Core::CurrentArmInterface().GetCPSR();
|
||||
|
||||
cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
|
||||
cpsr->child(0)->setText(
|
||||
1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
|
||||
cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State
|
||||
cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable
|
||||
cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable
|
||||
cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort
|
||||
cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianness
|
||||
cpsr->child(6)->setText(1,
|
||||
QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
|
||||
cpsr->child(7)->setText(1,
|
||||
QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
|
||||
cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
|
||||
cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle
|
||||
cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation
|
||||
cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow
|
||||
cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend
|
||||
cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero
|
||||
cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than
|
||||
}
|
||||
|
||||
void RegistersWidget::CreateVFPSystemRegisterChildren() {
|
||||
QTreeWidgetItem* const fpscr = new QTreeWidgetItem(QStringList("FPSCR"));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("IOC")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("DZC")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("OFC")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("UFC")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("IXC")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("IDC")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("IOE")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("DZE")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("OFE")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("UFE")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("IXE")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("IDE")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Length"))));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Stride"))));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Rounding Mode"))));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("FZ")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("DN")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("V")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("C")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("Z")));
|
||||
fpscr->addChild(new QTreeWidgetItem(QStringList("N")));
|
||||
|
||||
QTreeWidgetItem* const fpexc = new QTreeWidgetItem(QStringList("FPEXC"));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("IOC")));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("OFC")));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("UFC")));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("INV")));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList(tr("Vector Iteration Count"))));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("FP2V")));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("EN")));
|
||||
fpexc->addChild(new QTreeWidgetItem(QStringList("EX")));
|
||||
|
||||
vfp_system_registers->addChild(fpscr);
|
||||
vfp_system_registers->addChild(fpexc);
|
||||
vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST")));
|
||||
vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST2")));
|
||||
}
|
||||
|
||||
void RegistersWidget::UpdateVFPSystemRegisterValues() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
42
src/yuzu/debugger/registers.h
Normal file
42
src/yuzu/debugger/registers.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDockWidget>
|
||||
#include "ui_registers.h"
|
||||
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class EmuThread;
|
||||
|
||||
class RegistersWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RegistersWidget(QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void OnDebugModeEntered();
|
||||
void OnDebugModeLeft();
|
||||
|
||||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
void OnEmulationStopping();
|
||||
|
||||
private:
|
||||
void CreateCPSRChildren();
|
||||
void UpdateCPSRValues();
|
||||
|
||||
void CreateVFPSystemRegisterChildren();
|
||||
void UpdateVFPSystemRegisterValues();
|
||||
|
||||
Ui::ARMRegisters cpu_regs_ui;
|
||||
|
||||
QTreeWidget* tree;
|
||||
|
||||
QTreeWidgetItem* core_registers;
|
||||
QTreeWidgetItem* vfp_registers;
|
||||
QTreeWidgetItem* vfp_system_registers;
|
||||
QTreeWidgetItem* cpsr;
|
||||
};
|
||||
40
src/yuzu/debugger/registers.ui
Normal file
40
src/yuzu/debugger/registers.ui
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ARMRegisters</class>
|
||||
<widget class="QDockWidget" name="ARMRegisters">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>ARM Registers</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Register</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QKeyEvent>
|
||||
@@ -265,17 +264,8 @@ void GameList::ValidateEntry(const QModelIndex& item) {
|
||||
if (file_path.isEmpty())
|
||||
return;
|
||||
std::string std_file_path(file_path.toStdString());
|
||||
if (!FileUtil::Exists(std_file_path))
|
||||
if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path))
|
||||
return;
|
||||
if (FileUtil::IsDirectory(std_file_path)) {
|
||||
QDir dir(std_file_path.c_str());
|
||||
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
|
||||
if (matching_main.size() == 1) {
|
||||
emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Users usually want to run a diffrent game after closing one
|
||||
search_field->clear();
|
||||
emit GameChosen(file_path);
|
||||
@@ -373,19 +363,6 @@ static bool HasSupportedFileExtension(const std::string& file_name) {
|
||||
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
static bool IsExtractedNCAMain(const std::string& file_name) {
|
||||
return QFileInfo(file_name.c_str()).fileName() == "main";
|
||||
}
|
||||
|
||||
static QString FormatGameName(const std::string& physical_name) {
|
||||
QFileInfo file_info(physical_name.c_str());
|
||||
if (IsExtractedNCAMain(physical_name)) {
|
||||
return file_info.dir().path();
|
||||
} else {
|
||||
return QString::fromStdString(physical_name);
|
||||
}
|
||||
}
|
||||
|
||||
void GameList::RefreshGameDirectory() {
|
||||
if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
|
||||
NGLOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
||||
@@ -403,8 +380,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
return false; // Breaks the callback loop.
|
||||
|
||||
bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
if (!is_dir &&
|
||||
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
|
||||
if (!is_dir && HasSupportedFileExtension(physical_name)) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
if (!loader)
|
||||
return true;
|
||||
@@ -416,7 +392,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
emit EntryReady({
|
||||
new GameListItemPath(FormatGameName(physical_name), smdh, program_id),
|
||||
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <QMessageBox>
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -34,6 +33,7 @@
|
||||
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
|
||||
#include "yuzu/debugger/graphics/graphics_surface.h"
|
||||
#include "yuzu/debugger/profiler.h"
|
||||
#include "yuzu/debugger/registers.h"
|
||||
#include "yuzu/debugger/wait_tree.h"
|
||||
#include "yuzu/game_list.h"
|
||||
#include "yuzu/hotkeys.h"
|
||||
@@ -169,6 +169,15 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
debug_menu->addAction(microProfileDialog->toggleViewAction());
|
||||
#endif
|
||||
|
||||
registersWidget = new RegistersWidget(this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, registersWidget);
|
||||
registersWidget->hide();
|
||||
debug_menu->addAction(registersWidget->toggleViewAction());
|
||||
connect(this, &GMainWindow::EmulationStarting, registersWidget,
|
||||
&RegistersWidget::OnEmulationStarting);
|
||||
connect(this, &GMainWindow::EmulationStopping, registersWidget,
|
||||
&RegistersWidget::OnEmulationStopping);
|
||||
|
||||
graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
|
||||
graphicsBreakpointsWidget->hide();
|
||||
@@ -279,7 +288,6 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||
void GMainWindow::ConnectMenuEvents() {
|
||||
// File
|
||||
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
|
||||
connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder);
|
||||
connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
|
||||
&GMainWindow::OnMenuSelectGameListRoot);
|
||||
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
|
||||
@@ -327,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)
|
||||
@@ -360,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);
|
||||
@@ -452,12 +434,17 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
||||
// before the CPU continues
|
||||
connect(emu_thread.get(), &EmuThread::DebugModeEntered, registersWidget,
|
||||
&RegistersWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
|
||||
connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
|
||||
&WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
|
||||
connect(emu_thread.get(), &EmuThread::DebugModeLeft, registersWidget,
|
||||
&RegistersWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
|
||||
connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget,
|
||||
&WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
|
||||
|
||||
// Update the GUI
|
||||
registersWidget->OnDebugModeEntered();
|
||||
if (ui.action_Single_Window_Mode->isChecked()) {
|
||||
game_list->hide();
|
||||
}
|
||||
@@ -552,8 +539,6 @@ void GMainWindow::OnMenuLoadFile() {
|
||||
for (const auto& piece : game_list->supported_file_extensions)
|
||||
extensions += "*." + piece + " ";
|
||||
|
||||
extensions += "main ";
|
||||
|
||||
QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
|
||||
file_filter += ";;" + tr("All Files (*.*)");
|
||||
|
||||
@@ -566,18 +551,6 @@ void GMainWindow::OnMenuLoadFile() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuLoadFolder() {
|
||||
QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
|
||||
|
||||
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
|
||||
if (matching_main.size() == 1) {
|
||||
BootGame(dir.path() + DIR_SEP + matching_main[0]);
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Invalid Directory Selected"),
|
||||
tr("The directory you have selected does not contain a 'main' file."));
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuSelectGameListRoot() {
|
||||
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||
if (!dir_path.isEmpty()) {
|
||||
|
||||
@@ -19,6 +19,7 @@ class GraphicsSurfaceWidget;
|
||||
class GRenderWindow;
|
||||
class MicroProfileDialog;
|
||||
class ProfilerWidget;
|
||||
class RegistersWidget;
|
||||
class WaitTreeWidget;
|
||||
|
||||
namespace Tegra {
|
||||
@@ -78,7 +79,6 @@ private:
|
||||
void ConnectWidgetEvents();
|
||||
void ConnectMenuEvents();
|
||||
|
||||
bool SupportsRequiredGLExtensions();
|
||||
bool LoadROM(const QString& filename);
|
||||
void BootGame(const QString& filename);
|
||||
void ShutdownGame();
|
||||
@@ -123,7 +123,6 @@ private slots:
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenSaveFolder(u64 program_id);
|
||||
void OnMenuLoadFile();
|
||||
void OnMenuLoadFolder();
|
||||
/// Called whenever a user selects the "File->Select Game List Root" menu item
|
||||
void OnMenuSelectGameListRoot();
|
||||
void OnMenuRecentFile();
|
||||
@@ -163,6 +162,7 @@ private:
|
||||
// Debugger panes
|
||||
ProfilerWidget* profilerWidget;
|
||||
MicroProfileDialog* microProfileDialog;
|
||||
RegistersWidget* registersWidget;
|
||||
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
|
||||
GraphicsSurfaceWidget* graphicsSurfaceWidget;
|
||||
WaitTreeWidget* waitTreeWidget;
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="action_Load_File"/>
|
||||
<addaction name="action_Load_Folder"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Select_Game_List_Root"/>
|
||||
<addaction name="menu_recent_files"/>
|
||||
@@ -107,11 +106,6 @@
|
||||
<string>Load File...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Folder">
|
||||
<property name="text">
|
||||
<string>Load Folder...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Symbol_Map">
|
||||
<property name="text">
|
||||
<string>Load Symbol Map...</string>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user