Compare commits

..

6 Commits

Author SHA1 Message Date
greggameplayer
a3f22ebc18 last clang-format fix 2018-06-04 18:51:44 +02:00
greggameplayer
8995ad6f29 delete one other trailing whitespace 2018-06-04 18:44:45 +02:00
greggameplayer
241d3c1473 fix some clang-format 2018-06-04 18:36:11 +02:00
greggameplayer
b4043ea231 delete trailing whitespace 2018-06-04 00:07:02 +02:00
greggameplayer
0f7b1a2818 fix clang-format 2018-06-03 22:52:31 +02:00
greggameplayer
50115fefa6 Add some IoctlCommand with their params to nvhost_gpu 2018-06-03 22:49:43 +02:00
106 changed files with 2528 additions and 5750 deletions

View File

@@ -42,7 +42,3 @@ notifications:
webhooks:
urls:
- https://api.yuzu-emu.org/code/travis/notify
cache:
directories:
- $HOME/.ccache

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) \

View File

@@ -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

View File

@@ -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");

View File

@@ -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);

View File

@@ -40,8 +40,6 @@ add_library(core STATIC
hle/config_mem.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
hle/kernel/address_arbiter.h
hle/kernel/client_port.cpp
hle/kernel/client_port.h
hle/kernel/client_session.cpp
@@ -126,8 +124,6 @@ add_library(core STATIC
hle/service/audio/audren_u.h
hle/service/audio/codecctl.cpp
hle/service/audio/codecctl.h
hle/service/audio/hwopus.cpp
hle/service/audio/hwopus.h
hle/service/bcat/module.cpp
hle/service/bcat/module.h
hle/service/bcat/bcat.cpp
@@ -152,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
@@ -261,8 +255,6 @@ add_library(core STATIC
loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nca.cpp
loader/nca.h
loader/nro.cpp
loader/nro.h
loader/nso.cpp

View File

@@ -35,17 +35,6 @@ LoadDll LoadDll::g_load_dll;
} \
} while (0)
static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
GDBStub::BreakpointAddress bkpt =
GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
if (GDBStub::IsMemoryBreak() ||
(bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
auto core = static_cast<ARM_Unicorn*>(user_data);
core->RecordBreak(bkpt);
uc_emu_stop(uc);
}
}
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
@@ -78,10 +67,6 @@ ARM_Unicorn::ARM_Unicorn() {
uc_hook hook{};
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1));
if (GDBStub::IsServerEnabled()) {
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
last_bkpt_hit = false;
}
}
ARM_Unicorn::~ARM_Unicorn() {
@@ -170,11 +155,7 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
}
void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
void ARM_Unicorn::Step() {
@@ -187,18 +168,6 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
CoreTiming::AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
if (last_bkpt_hit) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if (last_bkpt_hit) {
last_bkpt_hit = false;
GDBStub::Break();
}
GDBStub::SendTrap(thread, 5);
}
}
void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
@@ -264,8 +233,3 @@ void ARM_Unicorn::PrepareReschedule() {
}
void ARM_Unicorn::ClearInstructionCache() {}
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt = bkpt;
last_bkpt_hit = true;
}

View File

@@ -7,7 +7,6 @@
#include <unicorn/unicorn.h>
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
class ARM_Unicorn final : public ARM_Interface {
public:
@@ -36,10 +35,7 @@ public:
void Step() override;
void ClearInstructionCache() override;
void PageTableChanged() override{};
void RecordBreak(GDBStub::BreakpointAddress bkpt);
private:
uc_engine* uc{};
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit;
};

View File

@@ -19,20 +19,13 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz
if (file.GetSize() < sizeof(Header))
return Loader::ResultStatus::Error;
file.Seek(offset, SEEK_SET);
// For cartridges, HFSs can get very large, so we need to calculate the size up to
// the actual content itself instead of just blindly reading in the entire file.
Header pfs_header;
if (!file.ReadBytes(&pfs_header, sizeof(Header)))
return Loader::ResultStatus::Error;
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
return Loader::ResultStatus::ErrorInvalidFormat;
}
bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0);
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
size_t metadata_size =
sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
@@ -57,12 +50,7 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data,
return Loader::ResultStatus::Error;
memcpy(&pfs_header, &file_data[offset], sizeof(Header));
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
return Loader::ResultStatus::ErrorInvalidFormat;
}
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0);
size_t entries_offset = offset + sizeof(Header);
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
@@ -85,21 +73,21 @@ u32 PartitionFilesystem::GetNumEntries() const {
return pfs_header.num_entries;
}
u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
u64 PartitionFilesystem::GetEntryOffset(int index) const {
if (index > GetNumEntries())
return 0;
return content_offset + pfs_entries[index].fs_entry.offset;
}
u64 PartitionFilesystem::GetEntrySize(u32 index) const {
u64 PartitionFilesystem::GetEntrySize(int index) const {
if (index > GetNumEntries())
return 0;
return pfs_entries[index].fs_entry.size;
}
std::string PartitionFilesystem::GetEntryName(u32 index) const {
std::string PartitionFilesystem::GetEntryName(int index) const {
if (index > GetNumEntries())
return "";
@@ -125,7 +113,7 @@ u64 PartitionFilesystem::GetFileSize(const std::string& name) const {
}
void PartitionFilesystem::Print() const {
NGLOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic);
NGLOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic.data());
NGLOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries);
for (u32 i = 0; i < pfs_header.num_entries; i++) {
NGLOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i,

View File

@@ -27,9 +27,9 @@ public:
Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0);
u32 GetNumEntries() const;
u64 GetEntryOffset(u32 index) const;
u64 GetEntrySize(u32 index) const;
std::string GetEntryName(u32 index) const;
u64 GetEntryOffset(int index) const;
u64 GetEntrySize(int index) const;
std::string GetEntryName(int index) const;
u64 GetFileOffset(const std::string& name) const;
u64 GetFileSize(const std::string& name) const;
@@ -37,7 +37,7 @@ public:
private:
struct Header {
u32_le magic;
std::array<char, 4> magic;
u32_le num_entries;
u32_le strtab_size;
INSERT_PADDING_BYTES(0x4);

View File

@@ -32,13 +32,9 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -141,17 +137,15 @@ static u8 command_buffer[GDB_BUFFER_SIZE];
static u32 command_length;
static u32 latest_signal = 0;
static bool step_break = false;
static bool memory_break = false;
static Kernel::Thread* current_thread = nullptr;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
static u16 gdbstub_port = 24689;
static bool halt_loop = true;
static bool step_loop = false;
static bool send_trap = false;
// If set to false, the server will never be started and no
// gdbstub-related functions will be executed.
@@ -171,53 +165,6 @@ static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
static Kernel::Thread* FindThreadById(int id) {
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto thread : threads) {
if (thread->GetThreadId() == id) {
current_thread = thread.get();
return current_thread;
}
}
}
return nullptr;
}
static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
if (!thread) {
return 0;
}
if (id < SP_REGISTER) {
return thread->context.cpu_registers[id];
} else if (id == SP_REGISTER) {
return thread->context.sp;
} else if (id == PC_REGISTER) {
return thread->context.pc;
} else if (id == CPSR_REGISTER) {
return thread->context.cpsr;
} else {
return 0;
}
}
static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
if (!thread) {
return;
}
if (id < SP_REGISTER) {
thread->context.cpu_registers[id] = val;
} else if (id == SP_REGISTER) {
thread->context.sp = val;
} else if (id == PC_REGISTER) {
thread->context.pc = val;
} else if (id == CPSR_REGISTER) {
thread->context.cpsr = val;
}
}
/**
* Turns hex string character into the equivalent byte.
*
@@ -246,7 +193,7 @@ static u8 NibbleToHex(u8 n) {
if (n < 0xA) {
return '0' + n;
} else {
return 'a' + n - 0xA;
return 'A' + n - 0xA;
}
}
@@ -492,8 +439,6 @@ static void SendReply(const char* reply) {
return;
}
NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply);
memset(command_buffer, 0, sizeof(command_buffer));
command_length = static_cast<u32>(strlen(reply));
@@ -538,22 +483,6 @@ static void HandleQuery() {
} else if (strncmp(query, "Xfer:features:read:target.xml:",
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
} else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto thread : threads) {
val += fmt::format("{:x}", thread->GetThreadId());
val += ",";
}
}
val.pop_back();
SendReply(val.c_str());
} else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
SendReply("l");
} else {
SendReply("");
}
@@ -561,40 +490,11 @@ static void HandleQuery() {
/// Handle set thread command from gdb client.
static void HandleSetThread() {
if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) {
int thread_id = -1;
if (command_buffer[2] != '-') {
thread_id = static_cast<int>(HexToInt(
command_buffer + 2,
command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/));
}
if (thread_id >= 1) {
current_thread = FindThreadById(thread_id);
}
if (!current_thread) {
thread_id = 1;
current_thread = FindThreadById(thread_id);
}
if (current_thread) {
SendReply("OK");
return;
}
if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 ||
memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) {
return SendReply("OK");
}
SendReply("E01");
}
/// Handle thread alive command from gdb client.
static void HandleThreadAlive() {
int thread_id = static_cast<int>(
HexToInt(command_buffer + 1,
command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
if (thread_id == 0) {
thread_id = 1;
}
if (FindThreadById(thread_id)) {
SendReply("OK");
return;
}
SendReply("E01");
}
@@ -603,24 +503,15 @@ static void HandleThreadAlive() {
*
* @param signal Signal to be sent to client.
*/
static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
static void SendSignal(u32 signal) {
if (gdbserver_socket == -1) {
return;
}
latest_signal = signal;
std::string buffer;
if (full) {
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER,
Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
Common::swap64(RegRead(SP_REGISTER, thread)));
} else {
buffer = fmt::format("T{:02x};", latest_signal);
}
buffer += fmt::format("thread:{:x};", thread->GetThreadId());
std::string buffer = fmt::format("T{:02x}", latest_signal);
NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer);
SendReply(buffer.c_str());
}
@@ -636,7 +527,7 @@ static void ReadCommand() {
} else if (c == 0x03) {
NGLOG_INFO(Debug_GDBStub, "gdb: found break command");
halt_loop = true;
SendSignal(current_thread, SIGTRAP);
SendSignal(SIGTRAP);
return;
} else if (c != GDB_STUB_START) {
NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
@@ -707,11 +598,11 @@ static void ReadRegister() {
}
if (id <= SP_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast<int>(id)));
} else if (id == PC_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
LongToGdbHex(reply, Core::CurrentArmInterface().GetPC());
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR());
} else {
return SendReply("E01");
}
@@ -727,16 +618,16 @@ static void ReadRegisters() {
u8* bufptr = buffer;
for (int reg = 0; reg <= SP_REGISTER; reg++) {
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg));
}
bufptr += (32 * 16);
LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC());
bufptr += 16;
IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread));
IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR());
bufptr += 8;
@@ -755,11 +646,11 @@ static void WriteRegister() {
}
if (id <= SP_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr));
} else if (id == PC_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr));
} else if (id == CPSR_REGISTER) {
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr));
} else {
return SendReply("E01");
}
@@ -776,11 +667,11 @@ static void WriteRegisters() {
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16));
} else if (reg == PC_REGISTER) {
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16));
} else if (reg == CPSR_REGISTER) {
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16));
} else {
UNIMPLEMENTED();
}
@@ -843,7 +734,7 @@ static void WriteMemory() {
void Break(bool is_memory_break) {
if (!halt_loop) {
halt_loop = true;
send_trap = true;
SendSignal(SIGTRAP);
}
memory_break = is_memory_break;
@@ -853,10 +744,10 @@ void Break(bool is_memory_break) {
static void Step() {
step_loop = true;
halt_loop = true;
send_trap = true;
step_break = true;
SendSignal(SIGTRAP);
}
/// Tell the CPU if we hit a memory breakpoint.
bool IsMemoryBreak() {
if (IsConnected()) {
return false;
@@ -868,6 +759,7 @@ bool IsMemoryBreak() {
/// Tell the CPU to continue executing.
static void Continue() {
memory_break = false;
step_break = false;
step_loop = false;
halt_loop = false;
}
@@ -1006,7 +898,7 @@ void HandlePacket() {
HandleSetThread();
break;
case '?':
SendSignal(current_thread, latest_signal);
SendSignal(latest_signal);
break;
case 'k':
Shutdown();
@@ -1043,9 +935,6 @@ void HandlePacket() {
case 'Z':
AddBreakpoint();
break;
case 'T':
HandleThreadAlive();
break;
default:
SendReply("");
break;
@@ -1190,11 +1079,4 @@ bool GetCpuStepFlag() {
void SetCpuStepFlag(bool is_step) {
step_loop = is_step;
}
void SendTrap(Kernel::Thread* thread, int trap) {
if (send_trap) {
send_trap = false;
SendSignal(thread, trap);
}
}
}; // namespace GDBStub

View File

@@ -7,7 +7,6 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/thread.h"
namespace GDBStub {
@@ -92,12 +91,4 @@ bool GetCpuStepFlag();
* @param is_step
*/
void SetCpuStepFlag(bool is_step);
/**
* Send trap signal from thread back to the gdbstub server.
*
* @param thread Sending thread.
* @param trap Trap no.
*/
void SendTrap(Kernel::Thread* thread, int trap);
} // namespace GDBStub

View File

@@ -1,173 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/memory.h"
namespace Kernel {
namespace AddressArbiter {
// Performs actual address waiting logic.
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->arb_wait_address = address;
current_thread->status = THREADSTATUS_WAIT_ARB;
current_thread->wakeup_callback = nullptr;
current_thread->WakeAfterDelay(timeout);
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
return RESULT_TIMEOUT;
}
// Gets the threads waiting on an address.
static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr address) {
auto RetrieveWaitingThreads =
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
for (auto& thread : thread_list) {
if (thread->arb_wait_address == arb_addr)
waiting_threads.push_back(thread);
}
};
// Retrieve a list of all threads that are waiting for this address.
RetrieveWaitingThreads(0, waiting_threads, address);
RetrieveWaitingThreads(1, waiting_threads, address);
RetrieveWaitingThreads(2, waiting_threads, address);
RetrieveWaitingThreads(3, waiting_threads, address);
// Sort them by priority, such that the highest priority ones come first.
std::sort(waiting_threads.begin(), waiting_threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
});
}
// Wake up num_to_wake (or all) threads in a vector.
static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all.
size_t last = waiting_threads.size();
if (num_to_wake > 0)
last = num_to_wake;
// Signal the waiting threads.
for (size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->arb_wait_address = 0;
waiting_threads[i]->ResumeFromWait();
}
}
// Signals an address being waited on.
ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
// Signals an address being waited on and increments its value if equal to the value argument.
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
// Ensure that we can write to the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (static_cast<s32>(Memory::Read32(address)) == value) {
Memory::Write32(address, static_cast<u32>(value + 1));
} else {
return ERR_INVALID_STATE;
}
return SignalToAddress(address, num_to_wake);
}
// Signals an address being waited on and modifies its value based on waiting thread count if equal
// to the value argument.
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
// Ensure that we can write to the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
// Determine the modified value depending on the waiting count.
s32 updated_value;
if (waiting_threads.size() == 0) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
updated_value = value + 1;
} else {
updated_value = value;
}
if (static_cast<s32>(Memory::Read32(address)) == value) {
Memory::Write32(address, static_cast<u32>(updated_value));
} else {
return ERR_INVALID_STATE;
}
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
// Waits on an address if the value passed is less than the argument value, optionally decrementing.
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
// Ensure that we can read the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
s32 cur_value = static_cast<s32>(Memory::Read32(address));
if (cur_value < value) {
Memory::Write32(address, static_cast<u32>(cur_value - 1));
} else {
return ERR_INVALID_STATE;
}
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
return RESULT_TIMEOUT;
}
return WaitForAddress(address, timeout);
}
// Waits on an address if the value passed is equal to the argument value.
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
// Ensure that we can read the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// Only wait for the address if equal.
if (static_cast<s32>(Memory::Read32(address)) != value) {
return ERR_INVALID_STATE;
}
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
return RESULT_TIMEOUT;
}
return WaitForAddress(address, timeout);
}
} // namespace AddressArbiter
} // namespace Kernel

View File

@@ -1,32 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/result.h"
namespace Kernel {
namespace AddressArbiter {
enum class ArbitrationType {
WaitIfLessThan = 0,
DecrementAndWaitIfLessThan = 1,
WaitIfEqual = 2,
};
enum class SignalType {
Signal = 0,
IncrementAndSignalIfEqual = 1,
ModifyByWaitingCountAndSignalIfEqual = 2,
};
ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
} // namespace AddressArbiter
} // namespace Kernel

View File

@@ -20,16 +20,13 @@ enum {
MaxConnectionsReached = 52,
// Confirmed Switch OS error codes
InvalidAddress = 102,
InvalidMemoryState = 106,
MisalignedAddress = 102,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
TooLarge = 119,
InvalidEnumValue = 120,
InvalidState = 125,
};
}
@@ -42,15 +39,14 @@ constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1);
constexpr ResultCode ERR_WRONG_PERMISSION(-1);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(-1);
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1);
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_ADDRESS(-1);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(-1);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_POINTER(-1);
constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
constexpr ResultCode ERR_NOT_AUTHORIZED(-1);

View File

@@ -271,11 +271,6 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
}
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const {
if (size == 0) {
NGLOG_WARNING(Core, "skip empty buffer write");
return 0;
}
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
const size_t buffer_size{GetWriteBufferSize(buffer_index)};
if (size > buffer_size) {

View File

@@ -59,7 +59,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
}
SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
@@ -97,7 +97,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
ResultCode Mutex::Release(VAddr address) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
}
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);

View File

@@ -11,7 +11,6 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/event.h"
@@ -317,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();
}
@@ -581,7 +575,7 @@ static void SleepThread(s64 nanoseconds) {
Core::System::GetInstance().PrepareReschedule();
}
/// Wait process wide key atomic
/// Signal process wide key atomic
static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr,
Handle thread_handle, s64 nano_seconds) {
NGLOG_TRACE(
@@ -690,58 +684,6 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
return RESULT_SUCCESS;
}
// Wait for an address (via Address Arbiter)
static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) {
NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
address, type, value, timeout);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// If the address is not properly aligned to 4 bytes, return invalid address.
if (address % sizeof(u32) != 0) {
return ERR_INVALID_ADDRESS;
}
switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
case AddressArbiter::ArbitrationType::WaitIfLessThan:
return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false);
case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
case AddressArbiter::ArbitrationType::WaitIfEqual:
return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
default:
return ERR_INVALID_ENUM_VALUE;
}
}
// Signals to an address (via Address Arbiter)
static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) {
NGLOG_WARNING(Kernel_SVC,
"called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address,
type, value, num_to_wake);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// If the address is not properly aligned to 4 bytes, return invalid address.
if (address % sizeof(u32) != 0) {
return ERR_INVALID_ADDRESS;
}
switch (static_cast<AddressArbiter::SignalType>(type)) {
case AddressArbiter::SignalType::Signal:
return AddressArbiter::SignalToAddress(address, num_to_wake);
case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
num_to_wake);
default:
return ERR_INVALID_ENUM_VALUE;
}
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
static u64 GetSystemTick() {
const u64 result{CoreTiming::GetTicks()};
@@ -802,7 +744,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
// Set the target CPU to the one specified in the process' exheader.
core = thread->owner_process->ideal_processor;
mask = 1ull << core;
mask = 1 << core;
}
if (mask == 0) {
@@ -819,7 +761,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
}
// Error out if the input core isn't enabled in the input mask.
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
}
@@ -914,8 +856,8 @@ static const FunctionDef SVC_Table[] = {
{0x31, nullptr, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
{0x34, nullptr, "WaitForAddress"},
{0x35, nullptr, "SignalToAddress"},
{0x36, nullptr, "Unknown"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},

View File

@@ -179,20 +179,6 @@ void SvcWrap() {
FuncReturn(retval);
}
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
FuncReturn(
func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3))
.raw);
}
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF),
(s32)(PARAM(3) & 0xFFFFFFFF))
.raw);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32

View File

@@ -140,11 +140,6 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
}
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == THREADSTATUS_WAIT_ARB);
thread->arb_wait_address = 0;
}
if (resume)
thread->ResumeFromWait();
}
@@ -184,7 +179,6 @@ void Thread::ResumeFromWait() {
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
case THREADSTATUS_WAIT_MUTEX:
case THREADSTATUS_WAIT_ARB:
break;
case THREADSTATUS_READY:

View File

@@ -45,7 +45,6 @@ enum ThreadStatus {
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
THREADSTATUS_WAIT_ARB, ///< Waiting due to a SignalToAddress/WaitForAddress svc
THREADSTATUS_DORMANT, ///< Created but not yet made ready
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
};
@@ -231,9 +230,6 @@ public:
VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
Handle wait_handle; ///< The handle used to wait for the mutex.
// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
std::string name;
/// Handle used by guest emulated application to access this thread

View File

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

View File

@@ -121,7 +121,6 @@ public:
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
void CreateStorage(Kernel::HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -138,7 +137,6 @@ private:
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx);
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {

View File

@@ -8,7 +8,6 @@
#include "core/hle/service/audio/audrec_u.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/codecctl.h"
#include "core/hle/service/audio/hwopus.h"
namespace Service::Audio {
@@ -18,7 +17,6 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AudRecU>()->InstallAsService(service_manager);
std::make_shared<AudRenU>()->InstallAsService(service_manager);
std::make_shared<CodecCtl>()->InstallAsService(service_manager);
std::make_shared<HwOpus>()->InstallAsService(service_manager);
}
} // namespace Service::Audio

View File

@@ -17,8 +17,7 @@ constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
IAudioRenderer(AudioRendererParameter audren_params)
: ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
IAudioRenderer() : ServiceFramework("IAudioRenderer") {
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioRendererSampleRate"},
{1, nullptr, "GetAudioRendererSampleCount"},
@@ -58,37 +57,27 @@ private:
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
UpdateDataHeader config{};
auto buf = ctx.ReadBuffer();
std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
NGLOG_DEBUG(Service_Audio, "{}", ctx.Description());
AudioRendererResponseData response_data{};
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
std::memcpy(mem_pool_info.data(),
buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
memory_pool_count * sizeof(MemoryPoolInfo));
response_data.section_0_size =
static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry));
response_data.section_1_size = static_cast<u32>(response_data.section_1.size());
response_data.section_2_size = static_cast<u32>(response_data.section_2.size());
response_data.section_3_size = static_cast<u32>(response_data.section_3.size());
response_data.section_4_size = static_cast<u32>(response_data.section_4.size());
response_data.section_5_size = static_cast<u32>(response_data.section_5.size());
response_data.total_size = sizeof(AudioRendererResponseData);
UpdateDataHeader response_data{worker_params};
ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
std::vector<u8> output(response_data.total_size);
std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (unsigned i = 0; i < memory_pool.size(); i++) {
if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
memory_pool[i].state = MemoryPoolStates::Attached;
else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
memory_pool[i].state = MemoryPoolStates::Detached;
else
memory_pool[i].state = mem_pool_info[i].pool_state;
for (unsigned i = 0; i < response_data.state_entries.size(); i++) {
// 4 = Busy and 5 = Ready?
response_data.state_entries[i].state = 5;
}
std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
ctx.WriteBuffer(output);
ctx.WriteBuffer(&response_data, response_data.total_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
@@ -120,66 +109,48 @@ private:
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
}
enum class MemoryPoolStates : u32 { // Should be LE
Invalid = 0x0,
Unknown = 0x1,
RequestDetach = 0x2,
Detached = 0x3,
RequestAttach = 0x4,
Attached = 0x5,
Released = 0x6,
};
struct MemoryPoolEntry {
MemoryPoolStates state;
struct AudioRendererStateEntry {
u32_le state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
static_assert(sizeof(AudioRendererStateEntry) == 0x10,
"AudioRendererStateEntry has wrong size");
struct MemoryPoolInfo {
u64_le pool_address;
u64_le pool_size;
MemoryPoolStates pool_state;
INSERT_PADDING_WORDS(3); // Unknown
};
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
struct UpdateDataHeader {
UpdateDataHeader() {}
UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
effects_size = config.effect_count * 0x10;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
voices_size + effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
struct AudioRendererResponseData {
u32_le unknown_0;
u32_le section_5_size;
u32_le section_0_size;
u32_le section_1_size;
u32_le unknown_10;
u32_le section_2_size;
u32_le unknown_18;
u32_le section_3_size;
u32_le section_4_size;
u32_le unknown_24;
u32_le unknown_28;
u32_le unknown_2c;
u32_le unknown_30;
u32_le unknown_34;
u32_le unknown_38;
u32_le total_size;
std::array<AudioRendererStateEntry, 0x18e> state_entries;
std::array<u8, 0x600> section_1;
std::array<u8, 0xe0> section_2;
std::array<u8, 0x20> section_3;
std::array<u8, 0x10> section_4;
std::array<u8, 0xb0> section_5;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
"AudioRendererResponseData has wrong size");
/// This is used to trigger the audio event callback.
CoreTiming::EventType* audio_event;
Kernel::SharedPtr<Kernel::Event> system_event;
AudioRendererParameter worker_params;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -277,33 +248,31 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
}
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params));
rb.PushIpcInterface<Audio::IAudioRenderer>();
NGLOG_DEBUG(Service_Audio, "called");
}
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioRendererParameter>();
auto params = rp.PopRaw<WorkerBufferParameters>();
u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
buffer_sz += params.unknown_c * 1024;
buffer_sz += 0x940 * (params.unknown_c + 1);
u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40);
buffer_sz += params.unknownC * 1024;
buffer_sz += 0x940 * (params.unknownC + 1);
buffer_sz += 0x3F0 * params.voice_count;
buffer_sz += Common::AlignUp(8 * (params.unknown_c + 1), 0x10);
buffer_sz += Common::AlignUp(8 * (params.unknownC + 1), 0x10);
buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
buffer_sz +=
Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) *
(params.unknown_8 + 6),
Common::AlignUp((0x3C0 * (params.sink_count + params.unknownC) + 4 * params.sample_count) *
(params.unknown8 + 6),
0x40);
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
u32 count = params.unknown_c + 1;
if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) {
u32 count = params.unknownC + 1;
u64 node_count = Common::AlignUp(count, 0x40);
u64 node_state_buffer_sz =
4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
@@ -318,20 +287,20 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
}
buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
buffer_sz += 0xE0 * params.unknown_2c;
if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) {
buffer_sz += 0xE0 * params.unknown2c;
buffer_sz += 0x20 * params.splitter_count;
buffer_sz += Common::AlignUp(4 * params.unknown_2c, 0x10);
buffer_sz += Common::AlignUp(4 * params.unknown2c, 0x10);
}
buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
((params.voice_count * 256) | 0x40);
if (params.unknown_1c >= 1) {
if (params.unknown1c >= 1) {
output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
16 * params.voice_count + 16) +
0x658) *
(params.unknown_1c + 1) +
(params.unknown1c + 1) +
0xc0,
0x40) +
output_sz;
@@ -359,7 +328,7 @@ bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
switch (feature) {
case AudioFeatures::Splitter:
return version_num >= 2u;
return version_num >= 2;
default:
return false;
}

View File

@@ -12,24 +12,6 @@ class HLERequestContext;
namespace Service::Audio {
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown_1c;
u8 unknown_20;
INSERT_PADDING_BYTES(3);
u32_le splitter_count;
u32_le unknown_2c;
INSERT_PADDING_WORDS(1);
u32_le revision;
};
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
class AudRenU final : public ServiceFramework<AudRenU> {
public:
explicit AudRenU();
@@ -40,6 +22,25 @@ private:
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDevice(Kernel::HLERequestContext& ctx);
struct WorkerBufferParameters {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown8;
u32_le unknownC;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown1c;
u8 unknown20;
u8 padding1[3];
u32_le splitter_count;
u32_le unknown2c;
u8 padding2[4];
u32_le magic;
};
static_assert(sizeof(WorkerBufferParameters) == 52,
"WorkerBufferParameters is an invalid size");
enum class AudioFeatures : u32 {
Splitter,
};

View File

@@ -1,29 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/hwopus.h"
namespace Service::Audio {
void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0x4000);
}
HwOpus::HwOpus() : ServiceFramework("hwopus") {
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
{2, nullptr, "InitializeMultiStream"},
{3, nullptr, "GetWorkBufferSizeMultiStream"},
};
RegisterHandlers(functions);
}
} // namespace Service::Audio

View File

@@ -1,20 +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::Audio {
class HwOpus final : public ServiceFramework<HwOpus> {
public:
explicit HwOpus();
~HwOpus() = default;
private:
void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Audio

View File

@@ -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);

View File

@@ -84,10 +84,6 @@ private:
for (size_t controller = 0; controller < mem.controllers.size(); controller++) {
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
// TODO(DarkLordZach): Is this layout/controller config actually invalid?
if (controller == Controller_Handheld && index == Layout_Single)
continue;
ControllerLayout& layout = mem.controllers[controller].layouts[index];
layout.header.num_entries = HID_NUM_ENTRIES;
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
@@ -98,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++;
@@ -106,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.

View File

@@ -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;

View File

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

View File

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

View File

@@ -4,8 +4,6 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -20,7 +18,7 @@ public:
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, nullptr, "Finalize"},
{2, &IUser::ListDevices, "ListDevices"},
{2, nullptr, "ListDevices"},
{3, nullptr, "StartDetection"},
{4, nullptr, "StopDetection"},
{5, nullptr, "Mount"},
@@ -35,116 +33,24 @@ public:
{14, nullptr, "GetRegisterInfo"},
{15, nullptr, "GetCommonInfo"},
{16, nullptr, "GetModelInfo"},
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{19, &IUser::GetState, "GetState"},
{20, &IUser::GetDeviceState, "GetDeviceState"},
{21, &IUser::GetNpadId, "GetNpadId"},
{17, nullptr, "AttachActivateEvent"},
{18, nullptr, "AttachDeactivateEvent"},
{19, nullptr, "GetState"},
{20, nullptr, "GetDeviceState"},
{21, nullptr, "GetNpadId"},
{22, nullptr, "GetApplicationArea2"},
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{23, nullptr, "AttachAvailabilityChangeEvent"},
{24, nullptr, "RecreateApplicationArea"},
};
RegisterHandlers(functions);
activate_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:ActivateEvent");
deactivate_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
availability_change_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
}
private:
enum class State : u32 {
NonInitialized = 0,
Initialized = 1,
};
enum class DeviceState : u32 {
Initialized = 0,
};
void Initialize(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
state = State::Initialized;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ListDevices(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 array_size = rp.Pop<u32>();
ctx.WriteBuffer(&device_handle, sizeof(device_handle));
NGLOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(activate_event);
}
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(deactivate_event);
}
void GetState(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(state));
}
void GetDeviceState(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(device_state));
}
void GetNpadId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(npad_id);
}
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(availability_change_event);
}
const u64 device_handle{0xDEAD};
const HID::ControllerID npad_id{HID::Controller_Player1};
State state{State::NonInitialized};
DeviceState device_state{DeviceState::Initialized};
Kernel::SharedPtr<Kernel::Event> activate_event;
Kernel::SharedPtr<Kernel::Event> deactivate_event;
Kernel::SharedPtr<Kernel::Event> availability_change_event;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {

View File

@@ -38,7 +38,7 @@ public:
{8, nullptr, "SetPriority"},
{9, nullptr, "SetNetworkProfileId"},
{10, nullptr, "SetRejectable"},
{11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"},
{11, nullptr, "SetConnectionConfirmationOption"},
{12, nullptr, "SetPersistent"},
{13, nullptr, "SetInstant"},
{14, nullptr, "SetSustainable"},
@@ -67,32 +67,23 @@ private:
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetResult(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event1, event2);
}
void Cancel(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
Kernel::SharedPtr<Kernel::Event> event1, event2;
};

View File

@@ -8,8 +8,6 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Service::Nvidia::Devices {
@@ -156,9 +154,6 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
// Remove this memory region from the rasterizer cache.
VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);

View File

@@ -26,10 +26,6 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
return ZCullGetInfo(input, output);
case IoctlCommand::IocZbcSetTable:
return ZBCSetTable(input, output);
case IoctlCommand::IocZbcQueryTable:
return ZBCQueryTable(input, output);
case IoctlCommand::IocFlushL2:
return FlushL2(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
@@ -140,22 +136,4 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcQueryTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
return 0;
}
u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlFlushL2 params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
return 0;
}
} // namespace Service::Nvidia::Devices

View File

@@ -26,18 +26,6 @@ private:
IocZcullGetCtxSizeCommand = 0x80044701,
IocZcullGetInfo = 0x80284702,
IocZbcSetTable = 0x402C4703,
IocZbcQueryTable = 0xC0344704,
IocFlushL2 = 0x40084707,
IocInvalICache = 0x4008470D,
IocSetMmudebugMode = 0x4008470E,
IocSetSmDebugMode = 0x4010470F,
IocWaitForPause = 0xC0084710,
IocGetTcpExceptionEnStatus = 0x80084711,
IocNumVsms = 0x80084712,
IocVsmsMapping = 0xC0044713,
IocGetErrorChannelUserData = 0xC008471B,
IocGetGpuTime = 0xC010471C,
IocGetCpuTimeCorrelationInfo = 0xC108471D,
};
struct IoctlGpuCharacteristics {
@@ -139,31 +127,12 @@ private:
};
static_assert(sizeof(IoctlZbcSetTable) == 44, "IoctlZbcSetTable is incorrect size");
struct IoctlZbcQueryTable {
u32_le color_ds[4];
u32_le color_l2[4];
u32_le depth;
u32_le ref_cnt;
u32_le format;
u32_le type;
u32_le index_size;
};
static_assert(sizeof(IoctlZbcQueryTable) == 52, "IoctlZbcQueryTable is incorrect size");
struct IoctlFlushL2 {
u32_le flush; // l2_flush | l2_invalidate << 1 | fb_flush << 2
u32_le reserved;
};
static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -121,9 +121,8 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
}
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
if (input.size() < sizeof(IoctlSubmitGpfifo))
UNIMPLEMENTED();
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",

View File

@@ -148,7 +148,6 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
}
u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
// TODO(Subv): These flags are unconfirmed.
enum FreeFlags {
Freed = 0,
NotFreedYet = 1,
@@ -162,21 +161,15 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = handles.find(params.handle);
ASSERT(itr != handles.end());
ASSERT(itr->second->refcount > 0);
itr->second->refcount--;
params.refcount = itr->second->refcount;
params.size = itr->second->size;
if (itr->second->refcount == 0) {
if (itr->second->refcount == 0)
params.flags = Freed;
// The address of the nvmap is written to the output if we're finally freeing it, otherwise
// 0 is written.
params.address = itr->second->addr;
} else {
else
params.flags = NotFreedYet;
params.address = 0;
}
handles.erase(params.handle);

View File

@@ -94,7 +94,7 @@ private:
struct IocFreeParams {
u32_le handle;
INSERT_PADDING_BYTES(4);
u64_le address;
u64_le refcount;
u32_le size;
u32_le flags;
};

View File

@@ -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);

View File

@@ -12,6 +12,9 @@
namespace Service::Set {
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
@@ -47,7 +50,7 @@ SET::SET() : ServiceFramework("set") {
{2, nullptr, "MakeLanguageCode"},
{3, nullptr, "GetAvailableLanguageCodeCount"},
{4, nullptr, "GetRegionCode"},
{5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
{5, nullptr, "GetAvailableLanguageCodes2"},
{6, nullptr, "GetAvailableLanguageCodeCount2"},
{7, nullptr, "GetKeyCodeMap"},
{8, nullptr, "GetQuestFlag"},

View File

@@ -9,7 +9,6 @@
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
#include "core/loader/nca.h"
#include "core/loader/nro.h"
#include "core/loader/nso.h"
@@ -33,7 +32,6 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
CHECK_TYPE(ELF)
CHECK_TYPE(NSO)
CHECK_TYPE(NRO)
CHECK_TYPE(NCA)
#undef CHECK_TYPE
@@ -59,8 +57,6 @@ FileType GuessFromExtension(const std::string& extension_) {
return FileType::NRO;
else if (extension == ".nso")
return FileType::NSO;
else if (extension == ".nca")
return FileType::NCA;
return FileType::Unknown;
}
@@ -73,8 +69,6 @@ const char* GetFileTypeString(FileType type) {
return "NRO";
case FileType::NSO:
return "NSO";
case FileType::NCA:
return "NCA";
case FileType::DeconstructedRomDirectory:
return "Directory";
case FileType::Error:
@@ -110,10 +104,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp
case FileType::NRO:
return std::make_unique<AppLoader_NRO>(std::move(file), filepath);
// NX NCA file format.
case FileType::NCA:
return std::make_unique<AppLoader_NCA>(std::move(file), filepath);
// NX deconstructed ROM directory.
case FileType::DeconstructedRomDirectory:
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath);

View File

@@ -29,7 +29,6 @@ enum class FileType {
ELF,
NSO,
NRO,
NCA,
DeconstructedRomDirectory,
};

View File

@@ -1,303 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nca.h"
#include "core/loader/nso.h"
#include "core/memory.h"
namespace Loader {
// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
constexpr u64 SECTION_HEADER_SIZE = 0x200;
constexpr u64 SECTION_HEADER_OFFSET = 0x400;
enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
struct NcaSectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
struct NcaHeader {
std::array<u8, 0x100> rsa_signature_1;
std::array<u8, 0x100> rsa_signature_2;
u32_le magic;
u8 is_system;
NcaContentType content_type;
u8 crypto_type;
u8 key_index;
u64_le size;
u64_le title_id;
INSERT_PADDING_BYTES(0x4);
u32_le sdk_version;
u8 crypto_type_2;
INSERT_PADDING_BYTES(15);
std::array<u8, 0x10> rights_id;
std::array<NcaSectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<std::array<u8, 0x10>, 0x4> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
struct NcaSectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NcaSectionFilesystemType filesystem_type;
u8 crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
struct Pfs0Superblock {
NcaSectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32_le size;
INSERT_PADDING_BYTES(4);
u64_le hash_table_offset;
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES(432);
};
static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
static bool IsValidNca(const NcaHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
// TODO(DarkLordZach): Add support for encrypted.
class Nca final {
std::vector<FileSys::PartitionFilesystem> pfs;
std::vector<u64> pfs_offset;
u64 romfs_offset = 0;
u64 romfs_size = 0;
boost::optional<u8> exefs_id = boost::none;
FileUtil::IOFile file;
std::string path;
u64 GetExeFsFileOffset(const std::string& file_name) const;
u64 GetExeFsFileSize(const std::string& file_name) const;
public:
ResultStatus Load(FileUtil::IOFile&& file, std::string path);
FileSys::PartitionFilesystem GetPfs(u8 id) const;
u64 GetRomFsOffset() const;
u64 GetRomFsSize() const;
std::vector<u8> GetExeFsFile(const std::string& file_name);
};
static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) {
// According to switchbrew, an exefs must only contain these two files:
return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0;
}
ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) {
file = std::move(in_file);
path = in_path;
file.Seek(0, SEEK_SET);
std::array<u8, sizeof(NcaHeader)> header_array{};
if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader)))
NGLOG_CRITICAL(Loader, "File reader errored out during header read.");
NcaHeader header{};
std::memcpy(&header, header_array.data(), sizeof(NcaHeader));
if (!IsValidNca(header))
return ResultStatus::ErrorInvalidFormat;
int number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NcaSectionTableEntry entry) { return entry.media_offset > 0; });
for (int i = 0; i < number_sections; ++i) {
// Seek to beginning of this section.
file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
std::array<u8, sizeof(NcaSectionHeaderBlock)> array{};
if (sizeof(NcaSectionHeaderBlock) !=
file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock)))
NGLOG_CRITICAL(Loader, "File reader errored out during header read.");
NcaSectionHeaderBlock block{};
std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock));
if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) {
romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
romfs_size =
header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset;
} else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) {
Pfs0Superblock sb{};
// Seek back to beginning of this section.
file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock)))
NGLOG_CRITICAL(Loader, "File reader errored out during header read.");
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
sb.pfs0_header_offset;
FileSys::PartitionFilesystem npfs{};
ResultStatus status = npfs.Load(path, offset);
if (status == ResultStatus::Success) {
pfs.emplace_back(std::move(npfs));
pfs_offset.emplace_back(offset);
}
}
}
for (size_t i = 0; i < pfs.size(); ++i) {
if (IsPfsExeFs(pfs[i]))
exefs_id = i;
}
return ResultStatus::Success;
}
FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const {
return pfs[id];
}
u64 Nca::GetExeFsFileOffset(const std::string& file_name) const {
if (exefs_id == boost::none)
return 0;
return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id];
}
u64 Nca::GetExeFsFileSize(const std::string& file_name) const {
if (exefs_id == boost::none)
return 0;
return pfs[*exefs_id].GetFileSize(file_name);
}
u64 Nca::GetRomFsOffset() const {
return romfs_offset;
}
u64 Nca::GetRomFsSize() const {
return romfs_size;
}
std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) {
std::vector<u8> out(GetExeFsFileSize(file_name));
file.Seek(GetExeFsFileOffset(file_name), SEEK_SET);
file.ReadBytes(out.data(), GetExeFsFileSize(file_name));
return out;
}
AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath)
: AppLoader(std::move(file)), filepath(std::move(filepath)) {}
FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) {
file.Seek(0, SEEK_SET);
std::array<u8, 0x400> header_enc_array{};
if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400))
return FileType::Error;
// TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
NcaHeader header{};
std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader));
if (IsValidNca(header) && header.content_type == NcaContentType::Program)
return FileType::NCA;
return FileType::Error;
}
ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (!file.IsOpen()) {
return ResultStatus::Error;
}
nca = std::make_unique<Nca>();
ResultStatus result = nca->Load(std::move(file), filepath);
if (result != ResultStatus::Success) {
return result;
}
result = metadata.Load(nca->GetExeFsFile("main.npdm"));
if (result != ResultStatus::Success) {
return result;
}
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
return ResultStatus::ErrorUnsupportedArch;
}
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);
if (next_load_addr) {
NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
} else {
next_load_addr = load_addr;
}
}
process->program_id = metadata.GetTitleID();
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
if (nca->GetRomFsSize() > 0)
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) {
if (nca->GetRomFsSize() == 0) {
NGLOG_DEBUG(Loader, "No RomFS available");
return ResultStatus::ErrorNotUsed;
}
romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
offset = nca->GetRomFsOffset();
size = nca->GetRomFsSize();
NGLOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
NGLOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
return ResultStatus::Success;
}
AppLoader_NCA::~AppLoader_NCA() = default;
} // namespace Loader

View File

@@ -1,49 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/loader.h"
namespace Loader {
class Nca;
/// Loads an NCA file
class AppLoader_NCA final : public AppLoader {
public:
AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
return IdentifyType(file, filepath);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) override;
~AppLoader_NCA();
private:
std::string filepath;
FileSys::ProgramMetadata metadata;
std::unique_ptr<Nca> nca;
};
} // namespace Loader

View File

@@ -66,22 +66,8 @@ FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&)
return FileType::Error;
}
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NsoSegmentHeader& header) {
std::vector<u8> uncompressed_data;
uncompressed_data.resize(header.size);
const int bytes_uncompressed = LZ4_decompress_safe(
reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()), compressed_data.size(), header.size);
ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header,
size_t compressed_size) {
int compressed_size) {
std::vector<u8> compressed_data;
compressed_data.resize(compressed_size);
@@ -91,65 +77,22 @@ static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeade
return {};
}
return DecompressSegment(compressed_data, header);
std::vector<u8> uncompressed_data;
uncompressed_data.resize(header.size);
const int bytes_uncompressed = LZ4_decompress_safe(
reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size);
ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data,
VAddr load_base) {
if (file_data.size() < sizeof(NsoHeader))
return {};
NsoHeader nso_header;
std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader));
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]);
for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j)
compressed_data[j] = file_data[nso_header.segments[i].offset + j];
std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;
codeset->segments[i].offset = nso_header.segments[i].location;
codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
}
// MOD header pointer is at .text offset + 4
u32 module_offset;
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
ModHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset->data.size += bss_size;
const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
program_image.resize(image_size);
// Load codeset for current process
codeset->name = name;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
return load_base + image_size;
}
VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
FileUtil::IOFile file(path, "rb");
if (!file.IsOpen()) {

View File

@@ -29,9 +29,6 @@ public:
return IdentifyType(file, filepath);
}
static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data,
VAddr load_base);
static VAddr LoadModule(const std::string& path, VAddr load_base);
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;

View File

@@ -241,10 +241,6 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr);
}
bool IsKernelVirtualAddress(const VAddr vaddr) {
return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
}
bool IsValidPhysicalAddress(const PAddr paddr) {
return GetPhysicalPointer(paddr) != nullptr;
}

View File

@@ -188,11 +188,6 @@ enum : VAddr {
MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
MAP_REGION_SIZE = 0x1000000000,
MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
/// Kernel Virtual Address Range
KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
KERNEL_REGION_SIZE = 0x7FFFE00000,
KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
};
/// Currently active page table
@@ -202,8 +197,6 @@ PageTable* GetCurrentPageTable();
/// Determines if the given VAddr is valid for the specified process.
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
bool IsValidVirtualAddress(const VAddr addr);
/// Determines if the given VAddr is a kernel address
bool IsKernelVirtualAddress(const VAddr addr);
bool IsValidPhysicalAddress(const PAddr addr);

View File

@@ -129,7 +129,6 @@ struct Values {
// Renderer
float resolution_factor;
bool toggle_framelimit;
bool use_accurate_framebuffers;
float bg_red;
float bg_green;

View File

@@ -161,8 +161,6 @@ TelemetrySession::TelemetrySession() {
Settings::values.resolution_factor);
AddField(Telemetry::FieldType::UserConfig, "Renderer_ToggleFramelimit",
Settings::values.toggle_framelimit);
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers",
Settings::values.use_accurate_framebuffers);
AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
Settings::values.use_docked_mode);
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -55,10 +55,8 @@ public:
virtual ~BreakPointObserver() {
auto context = context_weak.lock();
if (context) {
{
std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
context->breakpoint_observers.remove(this);
}
std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
context->breakpoint_observers.remove(this);
// If we are the last observer to be destroyed, tell the debugger context that
// it is free to continue. In particular, this is required for a proper yuzu

View File

@@ -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;

View File

@@ -126,10 +126,6 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
DrawArrays();
break;
}
case MAXWELL3D_REG_INDEX(clear_buffers): {
ProcessClearBuffers();
break;
}
case MAXWELL3D_REG_INDEX(query.query_get): {
ProcessQueryGet();
break;
@@ -160,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 {}",
@@ -177,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:
@@ -332,9 +313,8 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
Texture::FullTextureInfo tex_info{};
// TODO(Subv): Use the shader to determine which textures are actually accessed.
tex_info.index =
static_cast<u32>(current_texture - tex_info_buffer.address - TextureInfoOffset) /
sizeof(Texture::TextureHandle);
tex_info.index = (current_texture - tex_info_buffer.address - TextureInfoOffset) /
sizeof(Texture::TextureHandle);
// Load the TIC data.
if (tex_handle.tic_id != 0) {
@@ -359,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];
@@ -419,13 +365,5 @@ bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const {
UNREACHABLE();
}
void Maxwell3D::ProcessClearBuffers() {
ASSERT(regs.clear_buffers.R == regs.clear_buffers.G &&
regs.clear_buffers.R == regs.clear_buffers.B &&
regs.clear_buffers.R == regs.clear_buffers.A);
VideoCore::g_renderer->Rasterizer()->Clear();
}
} // namespace Engines
} // namespace Tegra

View File

@@ -280,34 +280,6 @@ public:
UnsignedInt = 0x2,
};
enum class ComparisonOp : u32 {
Never = 0x200,
Less = 0x201,
Equal = 0x202,
LessEqual = 0x203,
Greater = 0x204,
NotEqual = 0x205,
GreaterEqual = 0x206,
Always = 0x207,
};
struct Cull {
enum class FrontFace : u32 {
ClockWise = 0x0900,
CounterClockWise = 0x0901,
};
enum class CullFace : u32 {
Front = 0x0404,
Back = 0x0405,
FrontAndBack = 0x0408,
};
u32 enabled;
FrontFace front_face;
CullFace cull_face;
};
struct Blend {
enum class Equation : u32 {
Add = 1,
@@ -346,25 +318,6 @@ public:
Equation equation_a;
Factor factor_source_a;
Factor factor_dest_a;
INSERT_PADDING_WORDS(1);
};
struct RenderTargetConfig {
u32 address_high;
u32 address_low;
u32 width;
u32 height;
Tegra::RenderTargetFormat format;
u32 block_dimensions;
u32 array_mode;
u32 layer_stride;
u32 base_layer;
INSERT_PADDING_WORDS(7);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
};
union {
@@ -379,41 +332,32 @@ public:
INSERT_PADDING_WORDS(0x1B8);
RenderTargetConfig rt[NumRenderTargets];
struct {
u32 address_high;
u32 address_low;
u32 width;
u32 height;
Tegra::RenderTargetFormat format;
u32 block_dimensions;
u32 array_mode;
u32 layer_stride;
u32 base_layer;
INSERT_PADDING_WORDS(7);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} rt[NumRenderTargets];
struct {
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 {
@@ -427,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);
@@ -436,17 +389,12 @@ public:
u32 count;
} vertex_buffer;
INSERT_PADDING_WORDS(1);
float clear_color[4];
float clear_depth;
INSERT_PADDING_WORDS(0x93);
INSERT_PADDING_WORDS(0x99);
struct {
u32 address_high;
u32 address_low;
Tegra::DepthFormat format;
u32 format;
u32 block_dimensions;
u32 layer_stride;
@@ -468,41 +416,7 @@ public:
};
} rt_control;
INSERT_PADDING_WORDS(0x2B);
u32 depth_test_enable;
INSERT_PADDING_WORDS(0x5);
u32 independent_blend_enable;
u32 depth_write_enabled;
INSERT_PADDING_WORDS(0x8);
ComparisonOp depth_test_func;
INSERT_PADDING_WORDS(0xB);
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(0x2D);
u32 vb_element_base;
INSERT_PADDING_WORDS(0x49);
INSERT_PADDING_WORDS(0xCF);
struct {
u32 tsc_address_high;
@@ -583,27 +497,7 @@ public:
}
} index_array;
INSERT_PADDING_WORDS(0x7);
INSERT_PADDING_WORDS(0x46);
Cull cull;
INSERT_PADDING_WORDS(0x2B);
union {
u32 raw;
BitField<0, 1, u32> Z;
BitField<1, 1, u32> S;
BitField<2, 1, u32> R;
BitField<3, 1, u32> G;
BitField<4, 1, u32> B;
BitField<5, 1, u32> A;
BitField<6, 4, u32> RT;
BitField<10, 11, u32> layer;
} clear_buffers;
INSERT_PADDING_WORDS(0x4B);
INSERT_PADDING_WORDS(0xC7);
struct {
u32 query_address_high;
@@ -647,7 +541,9 @@ public:
} vertex_array[NumVertexArrays];
Blend independent_blend[NumRenderTargets];
Blend blend;
INSERT_PADDING_WORDS(0x39);
struct {
u32 limit_high;
@@ -752,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;
@@ -785,9 +678,6 @@ private:
/// Handles writes to the macro uploading registers.
void ProcessMacroUpload(u32 data);
/// Handles a write to the CLEAR_BUFFERS register.
void ProcessClearBuffers();
/// Handles a write to the QUERY_GET register.
void ProcessQueryGet();
@@ -810,27 +700,17 @@ ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport_transform[0], 0x280);
ASSERT_REG_POSITION(viewport, 0x300);
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
ASSERT_REG_POSITION(zeta, 0x3F8);
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
ASSERT_REG_POSITION(rt_control, 0x487);
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
ASSERT_REG_POSITION(blend, 0x4CF);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(code_address, 0x582);
ASSERT_REG_POSITION(draw, 0x585);
ASSERT_REG_POSITION(index_array, 0x5F2);
ASSERT_REG_POSITION(cull, 0x646);
ASSERT_REG_POSITION(clear_buffers, 0x674);
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);

View File

@@ -1,73 +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);
if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count * regs.y_count);
return;
}
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

View File

@@ -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

View File

@@ -109,6 +109,11 @@ union Sampler {
u64 value{};
};
union Uniform {
BitField<20, 14, u64> offset;
BitField<34, 5, u64> index;
};
} // namespace Shader
} // namespace Tegra
@@ -142,7 +147,6 @@ enum class PredCondition : u64 {
GreaterThan = 4,
NotEqual = 5,
GreaterEqual = 6,
NotEqualWithNan = 13,
// TODO(Subv): Other condition types
};
@@ -152,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,
@@ -166,31 +163,7 @@ enum class SubOp : u64 {
Lg2 = 0x3,
Rcp = 0x4,
Rsq = 0x5,
};
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,
Min = 0x8,
};
union Instruction {
@@ -209,19 +182,19 @@ union Instruction {
} pred;
BitField<19, 1, u64> negate_pred;
BitField<20, 8, Register> gpr20;
BitField<20, 4, SubOp> sub_op;
BitField<20, 7, SubOp> sub_op;
BitField<28, 8, Register> gpr28;
BitField<39, 8, Register> gpr39;
BitField<48, 16, u64> opcode;
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,96 +245,41 @@ 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 {
BitField<31, 4, u64> component_mask;
bool IsComponentEnabled(size_t component) const {
return ((1ull << component) & component_mask) != 0;
return ((1 << component) & component_mask) != 0;
}
} tex;
union {
BitField<50, 3, u64> component_mask_selector;
BitField<28, 8, Register> gpr28;
bool HasTwoDestinations() const {
return gpr28.Value() != Register::ZeroIndex;
}
bool IsComponentEnabled(size_t component) const {
static constexpr std::array<size_t, 5> one_dest_mask{0x1, 0x2, 0x4, 0x8, 0x3};
static constexpr std::array<size_t, 5> two_dest_mask{0x7, 0xb, 0xd, 0xe, 0xf};
const auto& mask{HasTwoDestinations() ? two_dest_mask : one_dest_mask};
ASSERT(component_mask_selector < mask.size());
return ((1ull << 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,30 +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,
ArithmeticImmediate,
ArithmeticInteger,
ArithmeticIntegerImmediate,
Bfe,
Shift,
Ffma,
Flow,
Memory,
FloatSet,
FloatSetPredicate,
IntegerSet,
IntegerSetPredicate,
PredicateSetPredicate,
Conversion,
@@ -635,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,14 +474,7 @@ private:
INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
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::ArithmeticImmediate, "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("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_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"),
@@ -671,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::ArithmeticImmediate, "MOV32_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"),
@@ -712,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) {

View File

@@ -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;

View File

@@ -15,22 +15,10 @@ namespace Tegra {
enum class RenderTargetFormat : u32 {
NONE = 0x0,
RGBA32_FLOAT = 0xC0,
RGBA32_UINT = 0xC2,
RGBA16_FLOAT = 0xCA,
RGB10_A2_UNORM = 0xD1,
RGBA8_UNORM = 0xD5,
RGBA8_SRGB = 0xD6,
R11G11B10_FLOAT = 0xE0,
};
enum class DepthFormat : u32 {
Z32_FLOAT = 0xA,
Z16_UNORM = 0x13,
S8_Z24_UNORM = 0x14,
Z24_X8_UNORM = 0x15,
Z24_S8_UNORM = 0x16,
Z24_C8_UNORM = 0x18,
};
/// Returns the number of bytes per pixel of each rendertarget format.
@@ -73,7 +61,6 @@ namespace Engines {
class Fermi2D;
class Maxwell3D;
class MaxwellCompute;
class MaxwellDMA;
} // namespace Engines
enum class EngineID {
@@ -114,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

View File

@@ -100,9 +100,9 @@ boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
boost::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
VAddr base_addr = PageSlot(gpu_addr);
ASSERT(base_addr != static_cast<u64>(PageStatus::Unmapped));
if (base_addr == static_cast<u64>(PageStatus::Allocated) ||
base_addr == static_cast<u64>(PageStatus::Unmapped)) {
if (base_addr == static_cast<u64>(PageStatus::Allocated)) {
return {};
}

View File

@@ -19,9 +19,6 @@ public:
/// Draw the current batch of vertex arrays
virtual void DrawArrays() = 0;
/// Clear the current framebuffer
virtual void Clear() = 0;
/// Notify rasterizer that the specified Maxwell register has been changed
virtual void NotifyMaxwellRegisterChanged(u32 method) = 0;
@@ -54,8 +51,9 @@ public:
}
/// Attempt to use a faster method to display the framebuffer to screen
virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
u32 pixel_stride, ScreenInfo& screen_info) {
virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
VAddr framebuffer_addr, u32 pixel_stride,
ScreenInfo& screen_info) {
return false;
}

View File

@@ -146,6 +146,7 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
u64 size = end - start + 1;
// Copy vertex array data
res_cache.FlushRegion(start, size, nullptr);
Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size);
// Bind the vertex array to the buffer at the current offset.
@@ -195,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. The constbuffer bindpoint starts after the shader stage configuration bind points.
u32 current_constbuffer_bindpoint = uniform_buffers.size();
u32 current_texture_bindpoint = 0;
// Next available bindpoint to use when uploading the const buffers to the GLSL shaders.
u32 current_constbuffer_bindpoint = 0;
for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) {
auto& shader_config = gpu.regs.shader_config[index];
@@ -213,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);
@@ -263,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();
@@ -297,19 +287,18 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
return true;
}
std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers() {
void RasterizerOpenGL::DrawArrays() {
if (accelerate_draw == AccelDraw::Disabled)
return;
MICROPROFILE_SCOPE(OpenGL_Drawing);
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
// Sync the depth test state before configuring the framebuffer surfaces.
SyncDepthTestState();
// TODO(bunnei): Implement this
// TODO(bunnei): Implement these
const bool has_stencil = false;
const bool using_color_fb = true;
const bool using_depth_fb = regs.zeta.Address() != 0;
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
const bool using_depth_fb = false;
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 ||
@@ -325,21 +314,35 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers() {
std::tie(color_surface, depth_surface, surfaces_rect) =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect);
const u16 res_scale = color_surface != nullptr
? color_surface->res_scale
: (depth_surface == nullptr ? 1u : depth_surface->res_scale);
MathUtil::Rectangle<u32> draw_rect{
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left,
surfaces_rect.left, surfaces_rect.right)), // Left
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top,
surfaces_rect.bottom, surfaces_rect.top)), // Top
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right,
surfaces_rect.left, surfaces_rect.right)), // Right
static_cast<u32>(
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom,
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale,
surfaces_rect.left, surfaces_rect.right)), // Left
static_cast<u32>(
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale,
surfaces_rect.bottom, surfaces_rect.top)), // Top
static_cast<u32>(
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale,
surfaces_rect.left, surfaces_rect.right)), // Right
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
viewport_rect.bottom * res_scale,
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
// Bind the framebuffer surfaces
BindFramebufferSurfaces(color_surface, depth_surface, has_stencil);
SyncViewport(surfaces_rect);
// Sync the viewport
SyncViewport(surfaces_rect, res_scale);
// 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
@@ -350,58 +353,6 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers() {
state.scissor.height = draw_rect.GetHeight();
state.Apply();
// Only return the surface to be marked as dirty if writing to it is enabled.
return std::make_pair(write_color_fb ? color_surface : nullptr,
write_depth_fb ? depth_surface : nullptr);
}
void RasterizerOpenGL::Clear() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
GLbitfield clear_mask = 0;
if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B &&
regs.clear_buffers.A) {
clear_mask |= GL_COLOR_BUFFER_BIT;
}
if (regs.clear_buffers.Z)
clear_mask |= GL_DEPTH_BUFFER_BIT;
if (clear_mask == 0)
return;
auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers();
// TODO(Subv): Support clearing only partial colors.
glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2],
regs.clear_color[3]);
glClearDepth(regs.clear_depth);
glClear(clear_mask);
// Mark framebuffer surfaces as dirty
if (dirty_color_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_depth_surface);
}
}
void RasterizerOpenGL::DrawArrays() {
if (accelerate_draw == AccelDraw::Disabled)
return;
MICROPROFILE_SCOPE(OpenGL_Drawing);
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers();
SyncBlendState();
SyncCullMode();
// TODO(bunnei): Sync framebuffer_scale uniform here
// TODO(bunnei): Sync scissorbox uniform(s) here
// Draw the vertex batch
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()};
@@ -458,16 +409,14 @@ void RasterizerOpenGL::DrawArrays() {
const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)};
if (is_indexed) {
const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
// Adjust the index buffer offset so it points to the first desired index.
index_buffer_offset += regs.index_array.first * regs.index_array.FormatSizeInBytes();
glDrawElementsBaseVertex(primitive_mode, regs.index_array.count,
MaxwellToGL::IndexFormat(regs.index_array.format),
reinterpret_cast<const void*>(index_buffer_offset), base_vertex);
const GLint index_min{static_cast<GLint>(regs.index_array.first)};
const GLint index_max{static_cast<GLint>(regs.index_array.first + regs.index_array.count)};
glDrawRangeElementsBaseVertex(primitive_mode, index_min, index_max, regs.index_array.count,
MaxwellToGL::IndexFormat(regs.index_array.format),
reinterpret_cast<const void*>(index_buffer_offset),
-index_min);
} else {
glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count);
glDrawArrays(primitive_mode, 0, regs.vertex_buffer.count);
}
// Disable scissor test
@@ -477,24 +426,90 @@ void RasterizerOpenGL::DrawArrays() {
// Unbind textures for potential future use as framebuffer attachments
for (auto& texture_unit : state.texture_units) {
texture_unit.Unbind();
texture_unit.texture_2d = 0;
}
state.Apply();
// Mark framebuffer surfaces as dirty
if (dirty_color_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_color_surface);
MathUtil::Rectangle<u32> draw_rect_unscaled{
draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale,
draw_rect.bottom / res_scale};
if (color_surface != nullptr && write_color_fb) {
auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_depth_surface);
if (depth_surface != nullptr && write_depth_fb) {
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
depth_surface);
}
}
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);
res_cache.FlushRegion(0, Kernel::VMManager::MAX_ADDRESS);
res_cache.FlushAll();
}
void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) {
@@ -504,13 +519,13 @@ void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) {
void RasterizerOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.InvalidateRegion(addr, size);
res_cache.InvalidateRegion(addr, size, nullptr);
}
void RasterizerOpenGL::FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushRegion(addr, size);
res_cache.InvalidateRegion(addr, size);
res_cache.InvalidateRegion(addr, size, nullptr);
}
bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) {
@@ -529,28 +544,45 @@ bool RasterizerOpenGL::AccelerateFill(const void* config) {
return true;
}
bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
VAddr framebuffer_addr, u32 pixel_stride,
ScreenInfo& screen_info) {
if (!framebuffer_addr) {
return {};
if (framebuffer_addr == 0) {
return false;
}
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)};
if (!surface) {
return {};
SurfaceParams src_params;
src_params.cpu_addr = framebuffer_addr;
src_params.addr = res_cache.TryFindFramebufferGpuAddress(framebuffer_addr).get_value_or(0);
src_params.width = std::min(framebuffer.width, pixel_stride);
src_params.height = framebuffer.height;
src_params.stride = pixel_stride;
src_params.is_tiled = true;
src_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
src_params.pixel_format =
SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format);
src_params.component_type =
SurfaceParams::ComponentTypeFromGPUPixelFormat(framebuffer.pixel_format);
src_params.UpdateParams();
MathUtil::Rectangle<u32> src_rect;
Surface src_surface;
std::tie(src_surface, src_rect) =
res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
if (src_surface == nullptr) {
return false;
}
// Verify that the cached surface is the same size and format as the requested framebuffer
const auto& params{surface->GetSurfaceParams()};
const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)};
ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different");
u32 scaled_width = src_surface->GetScaledWidth();
u32 scaled_height = src_surface->GetScaledHeight();
screen_info.display_texture = surface->Texture().handle;
screen_info.display_texcoords = MathUtil::Rectangle<float>(
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
(float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
screen_info.display_texture = src_surface->texture.handle;
return true;
}
@@ -622,95 +654,24 @@ 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);
size_t size = 0;
if (used_buffer.IsIndirect()) {
// Buffer is accessed indirectly, so upload the entire thing
size = buffer.size * sizeof(float);
if (size > MaxConstbufferSize) {
NGLOG_ERROR(HW_GPU, "indirect constbuffer size {} exceeds maximum {}", size,
MaxConstbufferSize);
size = MaxConstbufferSize;
}
} else {
// Buffer is accessed directly, upload just what we use
size = used_buffer.GetSize() * sizeof(float);
}
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
// UBO alignment requirements.
size = Common::AlignUp(size, sizeof(GLvec4));
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
std::vector<u8> data(size);
std::vector<u8> data(used_buffer.GetSize() * sizeof(float));
Memory::ReadBlock(*addr, data.data(), data.size());
glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo);
glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Now configure the bindpoint of the buffer inside the shader
std::string buffer_name = used_buffer.GetName();
GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str());
GLuint index =
glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, buffer_name.c_str());
if (index != -1)
glUniformBlockBinding(program, index, buffer_draw_state.bindpoint);
glShaderStorageBlockBinding(program, index, buffer_draw_state.bindpoint);
}
state.Apply();
return current_bindpoint + static_cast<u32>(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());
if (!texture.enabled) {
state.texture_units[current_bindpoint].texture_2d = 0;
continue;
}
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 + static_cast<u32>(entries.size());
return current_bindpoint + entries.size();
}
void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
@@ -719,16 +680,16 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
state.Apply();
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
color_surface != nullptr ? color_surface->Texture().handle : 0, 0);
color_surface != nullptr ? color_surface->texture.handle : 0, 0);
if (depth_surface != nullptr) {
if (has_stencil) {
// attach both depth and stencil
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
depth_surface->Texture().handle, 0);
depth_surface->texture.handle, 0);
} else {
// attach depth
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
depth_surface->Texture().handle, 0);
depth_surface->texture.handle, 0);
// clear stencil attachment
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
}
@@ -739,14 +700,14 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
}
}
void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) {
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;
state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom;
state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
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;
state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth() * res_scale);
state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight() * res_scale);
}
void RasterizerOpenGL::SyncClipEnabled() {
@@ -758,11 +719,7 @@ void RasterizerOpenGL::SyncClipCoef() {
}
void RasterizerOpenGL::SyncCullMode() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
state.cull.enabled = regs.cull.enabled != 0;
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
UNREACHABLE();
}
void RasterizerOpenGL::SyncDepthScale() {
@@ -773,29 +730,14 @@ void RasterizerOpenGL::SyncDepthOffset() {
UNREACHABLE();
}
void RasterizerOpenGL::SyncDepthTestState() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
state.depth.test_enabled = regs.depth_test_enable != 0;
state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
state.depth.test_func = MaxwellToGL::ComparisonOp(regs.depth_test_func);
void RasterizerOpenGL::SyncBlendEnabled() {
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::SyncBlendFuncs() {
UNREACHABLE();
}
void RasterizerOpenGL::SyncBlendColor() {
UNREACHABLE();
}

View File

@@ -7,7 +7,6 @@
#include <array>
#include <cstddef>
#include <memory>
#include <utility>
#include <vector>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -29,7 +28,6 @@ public:
~RasterizerOpenGL() override;
void DrawArrays() override;
void Clear() override;
void NotifyMaxwellRegisterChanged(u32 method) override;
void FlushAll() override;
void FlushRegion(Tegra::GPUVAddr addr, u64 size) override;
@@ -56,11 +54,6 @@ public:
OGLShader shader;
};
/// Maximum supported size that a constbuffer can have in bytes.
static constexpr size_t MaxConstbufferSize = 0x10000;
static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
"The maximum size of a constbuffer must be a multiple of the size of GLvec4");
private:
class SamplerInfo {
public:
@@ -83,14 +76,13 @@ private:
u32 border_color_a;
};
/// Configures the color and depth framebuffer states and returns the dirty <Color, Depth>
/// surfaces if writing was enabled.
std::pair<Surface, Surface> ConfigureFramebuffers();
/// Binds the framebuffer color and depth surface
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.
@@ -103,19 +95,8 @@ 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);
void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale);
/// Syncs the clip enabled status to match the guest state
void SyncClipEnabled();
@@ -132,11 +113,14 @@ private:
/// Syncs the depth offset to match the guest state
void SyncDepthOffset();
/// Syncs the depth test state to match the guest state
void SyncDepthTestState();
/// Syncs the blend enabled status to match the guest state
void SyncBlendEnabled();
/// Syncs the blend state to match the guest state
void SyncBlendState();
/// 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;

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,57 @@
// Copyright 2018 yuzu Emulator Project
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <map>
#include <memory>
#include <vector>
#include <set>
#include <tuple>
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#include <boost/icl/interval_map.hpp>
#include <boost/icl/interval_set.hpp>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#include <boost/optional.hpp>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/hash.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/textures/texture.h"
class CachedSurface;
struct CachedSurface;
using Surface = std::shared_ptr<CachedSurface>;
using SurfaceSet = std::set<Surface>;
using SurfaceRegions = boost::icl::interval_set<Tegra::GPUVAddr>;
using SurfaceMap = boost::icl::interval_map<Tegra::GPUVAddr, Surface>;
using SurfaceCache = boost::icl::interval_map<Tegra::GPUVAddr, SurfaceSet>;
using SurfaceInterval = SurfaceCache::interval_type;
static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() &&
std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(),
"incorrect interval types");
using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>;
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
using PageMap = boost::icl::interval_map<u64, int>;
enum class ScaleMatch {
Exact, // only accept same res scale
Upscale, // only allow higher scale than params
Ignore // accept every scaled res
};
struct SurfaceParams {
enum class PixelFormat {
ABGR8 = 0,
@@ -29,22 +60,12 @@ struct SurfaceParams {
A1B5G5R5 = 3,
R8 = 4,
RGBA16F = 5,
R11FG11FB10F = 6,
RGBA32UI = 7,
DXT1 = 8,
DXT23 = 9,
DXT45 = 10,
DXN1 = 11, // This is also known as BC4
ASTC_2D_4X4 = 12,
DXT1 = 6,
DXT23 = 7,
DXT45 = 8,
DXN1 = 9, // This is also known as BC4
MaxColorFormat,
// DepthStencil formats
Z24S8 = 13,
MaxDepthStencilFormat,
Max = MaxDepthStencilFormat,
Max,
Invalid = 255,
};
@@ -70,10 +91,10 @@ struct SurfaceParams {
/**
* Gets the compression factor for the specified PixelFormat. This applies to just the
* "compressed width" and "compressed height", not the overall compression factor of a
* compressed image. This is used for maintaining proper surface sizes for compressed
* texture formats.
* compressed image. This is used for maintaining proper surface sizes for compressed texture
* formats.
*/
static constexpr u32 GetCompressionFactor(PixelFormat format) {
static constexpr u32 GetCompresssionFactor(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
@@ -83,20 +104,19 @@ struct SurfaceParams {
1, // A2B10G10R10
1, // A1B5G5R5
1, // R8
1, // RGBA16F
1, // R11FG11FB10F
1, // RGBA32UI
2, // RGBA16F
4, // DXT1
4, // DXT23
4, // DXT45
4, // DXN1
4, // ASTC_2D_4X4
1, // Z24S8
}};
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
return compression_factor_table[static_cast<size_t>(format)];
}
u32 GetCompresssionFactor() const {
return GetCompresssionFactor(pixel_format);
}
static constexpr u32 GetFormatBpp(PixelFormat format) {
if (format == PixelFormat::Invalid)
@@ -109,14 +129,10 @@ struct SurfaceParams {
16, // A1B5G5R5
8, // R8
64, // RGBA16F
32, // R11FG11FB10F
128, // RGBA32UI
64, // DXT1
128, // DXT23
128, // DXT45
64, // DXN1
32, // ASTC_2D_4X4
32, // Z24S8
}};
ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -126,16 +142,6 @@ struct SurfaceParams {
return GetFormatBpp(pixel_format);
}
static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::Z24_S8_UNORM:
return PixelFormat::Z24S8;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
switch (format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
@@ -145,10 +151,16 @@ struct SurfaceParams {
return PixelFormat::A2B10G10R10;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
return PixelFormat::RGBA16F;
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
return PixelFormat::R11FG11FB10F;
case Tegra::RenderTargetFormat::RGBA32_UINT:
return PixelFormat::RGBA32UI;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
switch (format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
return PixelFormat::ABGR8;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -170,10 +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::R32_G32_B32_A32:
return PixelFormat::RGBA32UI;
case Tegra::Texture::TextureFormat::DXT1:
return PixelFormat::DXT1;
case Tegra::Texture::TextureFormat::DXT23:
@@ -182,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();
@@ -205,10 +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::RGBA32UI:
return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
case PixelFormat::DXT1:
return Tegra::Texture::TextureFormat::DXT1;
case PixelFormat::DXT23:
@@ -217,17 +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();
}
}
static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) {
switch (format) {
case PixelFormat::Z24S8:
return Tegra::DepthFormat::Z24_S8_UNORM;
default:
UNREACHABLE();
}
@@ -252,29 +243,17 @@ struct SurfaceParams {
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
return ComponentType::UNorm;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
return ComponentType::Float;
case Tegra::RenderTargetFormat::RGBA32_UINT:
return ComponentType::UInt;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
static ComponentType ComponentTypeFromGPUPixelFormat(
Tegra::FramebufferConfig::PixelFormat format) {
switch (format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
return PixelFormat::ABGR8;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::Z24_S8_UNORM:
return ComponentType::UNorm;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
@@ -282,14 +261,28 @@ struct SurfaceParams {
}
}
static SurfaceType GetFormatType(PixelFormat pixel_format) {
if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxColorFormat)) {
return SurfaceType::ColorTexture;
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
SurfaceType a_type = GetFormatType(pixel_format_a);
SurfaceType b_type = GetFormatType(pixel_format_b);
if (a_type == SurfaceType::ColorTexture && b_type == SurfaceType::ColorTexture) {
return true;
}
if (static_cast<size_t>(pixel_format) <
static_cast<size_t>(PixelFormat::MaxDepthStencilFormat)) {
return SurfaceType::DepthStencil;
if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) {
return true;
}
if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) {
return true;
}
return false;
}
static SurfaceType GetFormatType(PixelFormat pixel_format) {
if (static_cast<size_t>(pixel_format) < MaxPixelFormat) {
return SurfaceType::ColorTexture;
}
// TODO(Subv): Implement the other formats
@@ -298,101 +291,168 @@ struct SurfaceParams {
return SurfaceType::Invalid;
}
/// Returns the rectangle corresponding to this surface
MathUtil::Rectangle<u32> GetRect() const;
/// Returns the size of this surface in bytes, adjusted for compression
size_t SizeInBytes() const {
const u32 compression_factor{GetCompressionFactor(pixel_format)};
ASSERT(width % compression_factor == 0);
ASSERT(height % compression_factor == 0);
return (width / compression_factor) * (height / compression_factor) *
GetFormatBpp(pixel_format) / CHAR_BIT;
/// Update the params "size", "end" and "type" from the already set "addr", "width", "height"
/// and "pixel_format"
void UpdateParams() {
if (stride == 0) {
stride = width;
}
type = GetFormatType(pixel_format);
size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
: BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
end = addr + size;
}
SurfaceInterval GetInterval() const {
return SurfaceInterval::right_open(addr, end);
}
// Returns the outer rectangle containing "interval"
SurfaceParams FromInterval(SurfaceInterval interval) const;
SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
// Returns the region of the biggest valid rectange within interval
SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
/**
* Gets the actual width (in pixels) of the surface. This is provided because `width` is used
* for tracking the surface region in memory, which may be compressed for certain formats. In
* this scenario, `width` is actually the compressed width.
*/
u32 GetActualWidth() const {
return width * GetCompresssionFactor();
}
/**
* Gets the actual height (in pixels) of the surface. This is provided because `height` is used
* for tracking the surface region in memory, which may be compressed for certain formats. In
* this scenario, `height` is actually the compressed height.
*/
u32 GetActualHeight() const {
return height * GetCompresssionFactor();
}
u32 GetScaledWidth() const {
return width * res_scale;
}
u32 GetScaledHeight() const {
return height * res_scale;
}
MathUtil::Rectangle<u32> GetRect() const {
return {0, height, width, 0};
}
MathUtil::Rectangle<u32> GetScaledRect() const {
return {0, GetScaledHeight(), GetScaledWidth(), 0};
}
u64 PixelsInBytes(u64 size) const {
return size * CHAR_BIT / GetFormatBpp(pixel_format);
}
u64 BytesInPixels(u64 pixels) const {
return pixels * GetFormatBpp(pixel_format) / CHAR_BIT;
}
/// Returns the CPU virtual address for this surface
VAddr GetCpuAddr() const;
/// Returns true if the specified region overlaps with this surface's region in Switch memory
bool IsOverlappingRegion(Tegra::GPUVAddr region_addr, size_t region_size) const {
return addr <= (region_addr + region_size) && region_addr <= (addr + size_in_bytes);
}
bool ExactMatch(const SurfaceParams& other_surface) const;
bool CanSubRect(const SurfaceParams& sub_surface) const;
bool CanExpand(const SurfaceParams& expanded_surface) const;
bool CanTexCopy(const SurfaceParams& texcopy_params) const;
/// Creates SurfaceParams from a texture configation
static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
/// Creates SurfaceParams from a framebuffer configation
static SurfaceParams CreateForFramebuffer(
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config);
Tegra::GPUVAddr addr = 0;
Tegra::GPUVAddr end = 0;
boost::optional<VAddr> cpu_addr;
u64 size = 0;
Tegra::GPUVAddr addr;
bool is_tiled;
u32 block_height;
PixelFormat pixel_format;
ComponentType component_type;
SurfaceType type;
u32 width;
u32 height;
u32 unaligned_height;
size_t size_in_bytes;
u32 width = 0;
u32 height = 0;
u32 stride = 0;
u32 block_height = 0;
u16 res_scale = 1;
bool is_tiled = false;
PixelFormat pixel_format = PixelFormat::Invalid;
SurfaceType type = SurfaceType::Invalid;
ComponentType component_type = ComponentType::Invalid;
};
/// Hashable variation of SurfaceParams, used for a key in the surface cache
struct SurfaceKey : Common::HashableStruct<SurfaceParams> {
static SurfaceKey Create(const SurfaceParams& params) {
SurfaceKey res;
res.state = params;
return res;
}
};
struct CachedSurface : SurfaceParams {
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
namespace std {
template <>
struct hash<SurfaceKey> {
size_t operator()(const SurfaceKey& k) const {
return k.Hash();
}
};
} // namespace std
class CachedSurface final {
public:
CachedSurface(const SurfaceParams& params);
const OGLTexture& Texture() const {
return texture;
bool IsRegionValid(SurfaceInterval interval) const {
return (invalid_regions.find(interval) == invalid_regions.end());
}
static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
if (format == SurfaceParams::PixelFormat::Invalid)
bool IsSurfaceFullyInvalid() const {
return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval());
}
bool registered = false;
SurfaceRegions invalid_regions;
u64 fill_size = 0; /// Number of bytes to read from fill_data
std::array<u8, 4> fill_data;
OGLTexture texture;
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
}
const SurfaceParams& GetSurfaceParams() const {
return params;
}
std::unique_ptr<u8[]> gl_buffer;
size_t gl_buffer_size = 0;
// Read/Write data in Switch memory to/from gl_buffer
void LoadGLBuffer();
void FlushGLBuffer();
void LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr load_end);
void FlushGLBuffer(Tegra::GPUVAddr flush_start, Tegra::GPUVAddr flush_end);
// Upload/Download data in gl_buffer in/to this surface's texture
void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
private:
OGLTexture texture;
std::vector<u8> gl_buffer;
SurfaceParams params;
void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
GLuint draw_fb_handle);
void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
GLuint draw_fb_handle);
};
class RasterizerCacheOpenGL final : NonCopyable {
class RasterizerCacheOpenGL : NonCopyable {
public:
RasterizerCacheOpenGL();
~RasterizerCacheOpenGL();
/// Blit one surface's texture to another
bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect,
GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect);
/// Copy one surface's region to another
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
SurfaceInterval copy_interval);
/// Load a texture from Switch memory to OpenGL and cache it (if not already cached)
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Tries to find a framebuffer GPU address based on the provided CPU address
boost::optional<Tegra::GPUVAddr> TryFindFramebufferGpuAddress(VAddr cpu_addr) const;
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
/// Switch memory to OpenGL and caches it (if not already cached)
SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Get a surface based on the texture configuration
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
@@ -400,21 +460,29 @@ public:
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const MathUtil::Rectangle<s32>& viewport);
/// Marks the specified surface as "dirty", in that it is out of sync with Switch memory
void MarkSurfaceAsDirty(const Surface& surface);
/// Get a surface that matches the fill config
Surface GetFillSurface(const void* config);
/// Tries to find a framebuffer GPU address based on the provided CPU address
Surface TryFindFramebufferSurface(VAddr cpu_addr) const;
/// Get a surface that matches a "texture copy" display transfer config
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
/// Write any cached resources overlapping the region back to memory (if dirty)
void FlushRegion(Tegra::GPUVAddr addr, size_t size);
void FlushRegion(Tegra::GPUVAddr addr, u64 size, Surface flush_surface = nullptr);
/// Mark the specified region as being invalidated
void InvalidateRegion(Tegra::GPUVAddr addr, size_t size);
/// Mark region as being invalidated by region_owner (nullptr if Switch memory)
void InvalidateRegion(Tegra::GPUVAddr addr, u64 size, const Surface& region_owner);
/// Flush all cached resources tracked by this cache manager
void FlushAll();
private:
void LoadSurface(const Surface& surface);
Surface GetSurface(const SurfaceParams& params);
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
/// Update surface's texture for given region when necessary
void ValidateSurface(const Surface& surface, Tegra::GPUVAddr addr, u64 size);
/// Create a new surface
Surface CreateSurface(const SurfaceParams& params);
/// Register surface into the cache
void RegisterSurface(const Surface& surface);
@@ -425,9 +493,18 @@ private:
/// Increase/decrease the number of surface in pages touching the specified region
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta);
std::unordered_map<SurfaceKey, Surface> surface_cache;
SurfaceCache surface_cache;
PageMap cached_pages;
SurfaceMap dirty_regions;
SurfaceSet remove_surfaces;
OGLFramebuffer read_framebuffer;
OGLFramebuffer draw_framebuffer;
OGLVertexArray attributeless_vao;
OGLBuffer d24s8_abgr_buffer;
GLsizeiptr d24s8_abgr_buffer_size;
OGLProgram d24s8_abgr_shader;
GLint d24s8_abgr_tbo_size_u_id;
GLint d24s8_abgr_viewport_u_id;
};

View File

@@ -38,7 +38,7 @@ public:
if (handle == 0)
return;
glDeleteTextures(1, &handle);
OpenGLState::GetCurState().UnbindTexture(handle).Apply();
OpenGLState::GetCurState().ResetTexture(handle).Apply();
handle = 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}

View File

@@ -22,28 +22,17 @@ class ConstBufferEntry {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
public:
void MarkAsUsed(u64 index, u64 offset, Maxwell::ShaderStage stage) {
void MarkAsUsed(unsigned index, unsigned offset, Maxwell::ShaderStage stage) {
is_used = true;
this->index = static_cast<unsigned>(index);
this->stage = stage;
max_offset = std::max(max_offset, static_cast<unsigned>(offset));
}
void MarkAsUsedIndirect(u64 index, Maxwell::ShaderStage stage) {
is_used = true;
is_indirect = true;
this->index = static_cast<unsigned>(index);
this->index = index;
this->stage = stage;
max_offset = std::max(max_offset, offset);
}
bool IsUsed() const {
return is_used;
}
bool IsIndirect() const {
return is_indirect;
}
unsigned GetIndex() const {
return index;
}
@@ -62,54 +51,13 @@ private:
};
bool is_used{};
bool is_indirect{};
unsigned index{};
unsigned max_offset{};
Maxwell::ShaderStage stage;
};
class SamplerEntry {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
public:
SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index)
: offset(offset), stage(stage), sampler_index(index) {}
size_t GetOffset() const {
return offset;
}
size_t GetIndex() const {
return sampler_index;
}
Maxwell::ShaderStage GetStage() const {
return stage;
}
std::string GetName() const {
return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' +
std::to_string(sampler_index) + ']';
}
static std::string GetArrayName(Maxwell::ShaderStage stage) {
return TextureSamplerNames[static_cast<size_t>(stage)];
}
private:
static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
"tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
};
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
/// instruction.
size_t offset;
Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
};
struct ShaderEntries {
std::vector<ConstBufferEntry> const_buffer_entries;
std::vector<SamplerEntry> texture_samplers;
};
using ProgramResult = std::pair<std::string, ShaderEntries>;

View File

@@ -32,14 +32,33 @@ 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) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): Support more than one viewport
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0;
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0;
}
} // namespace GLShader

View File

@@ -45,6 +45,7 @@ public:
shader.Create(program_result.first.c_str(), type);
program.Create(true, shader.handle);
Impl::SetShaderUniformBlockBindings(program.handle);
Impl::SetShaderSamplerBindings(program.handle);
entries = program_result.second;
}
GLuint GetHandle() const {

View File

@@ -48,9 +48,20 @@ OpenGLState::OpenGLState() {
logic_op = GL_COPY;
for (auto& texture_unit : texture_units) {
texture_unit.Reset();
texture_unit.texture_2d = 0;
texture_unit.sampler = 0;
}
lighting_lut.texture_buffer = 0;
fog_lut.texture_buffer = 0;
proctex_lut.texture_buffer = 0;
proctex_diff_lut.texture_buffer = 0;
proctex_color_map.texture_buffer = 0;
proctex_alpha_map.texture_buffer = 0;
proctex_noise_lut.texture_buffer = 0;
draw.read_framebuffer = 0;
draw.draw_framebuffer = 0;
draw.vertex_array = 0;
@@ -181,22 +192,13 @@ void OpenGLState::Apply() const {
}
// Textures
for (int i = 0; i < std::size(texture_units); ++i) {
for (size_t i = 0; i < std::size(texture_units); ++i) {
if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
glActiveTexture(TextureUnits::MaxwellTexture(i).Enum());
glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
}
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
glBindSampler(static_cast<GLuint>(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());
glBindSampler(i, texture_units[i].sampler);
}
}
@@ -208,12 +210,54 @@ void OpenGLState::Apply() const {
if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint ||
current.ssbo != new_state.ssbo) {
if (new_state.enabled) {
glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, new_state.bindpoint, new_state.ssbo);
}
}
}
}
// Lighting LUTs
if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
glActiveTexture(TextureUnits::LightingLUT.Enum());
glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer);
}
// Fog LUT
if (fog_lut.texture_buffer != cur_state.fog_lut.texture_buffer) {
glActiveTexture(TextureUnits::FogLUT.Enum());
glBindTexture(GL_TEXTURE_BUFFER, fog_lut.texture_buffer);
}
// ProcTex Noise LUT
if (proctex_noise_lut.texture_buffer != cur_state.proctex_noise_lut.texture_buffer) {
glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum());
glBindTexture(GL_TEXTURE_BUFFER, proctex_noise_lut.texture_buffer);
}
// ProcTex Color Map
if (proctex_color_map.texture_buffer != cur_state.proctex_color_map.texture_buffer) {
glActiveTexture(TextureUnits::ProcTexColorMap.Enum());
glBindTexture(GL_TEXTURE_BUFFER, proctex_color_map.texture_buffer);
}
// ProcTex Alpha Map
if (proctex_alpha_map.texture_buffer != cur_state.proctex_alpha_map.texture_buffer) {
glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum());
glBindTexture(GL_TEXTURE_BUFFER, proctex_alpha_map.texture_buffer);
}
// ProcTex LUT
if (proctex_lut.texture_buffer != cur_state.proctex_lut.texture_buffer) {
glActiveTexture(TextureUnits::ProcTexLUT.Enum());
glBindTexture(GL_TEXTURE_BUFFER, proctex_lut.texture_buffer);
}
// ProcTex Diff LUT
if (proctex_diff_lut.texture_buffer != cur_state.proctex_diff_lut.texture_buffer) {
glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());
glBindTexture(GL_TEXTURE_BUFFER, proctex_diff_lut.texture_buffer);
}
// Framebuffer
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -281,12 +325,26 @@ void OpenGLState::Apply() const {
cur_state = *this;
}
OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
OpenGLState& OpenGLState::ResetTexture(GLuint handle) {
for (auto& unit : texture_units) {
if (unit.texture_2d == handle) {
unit.Unbind();
unit.texture_2d = 0;
}
}
if (lighting_lut.texture_buffer == handle)
lighting_lut.texture_buffer = 0;
if (fog_lut.texture_buffer == handle)
fog_lut.texture_buffer = 0;
if (proctex_noise_lut.texture_buffer == handle)
proctex_noise_lut.texture_buffer = 0;
if (proctex_color_map.texture_buffer == handle)
proctex_color_map.texture_buffer = 0;
if (proctex_alpha_map.texture_buffer == handle)
proctex_alpha_map.texture_buffer = 0;
if (proctex_lut.texture_buffer == handle)
proctex_lut.texture_buffer = 0;
if (proctex_diff_lut.texture_buffer == handle)
proctex_diff_lut.texture_buffer = 0;
return *this;
}

View File

@@ -85,27 +85,36 @@ 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;
void Unbind() {
texture_2d = 0;
swizzle.r = GL_RED;
swizzle.g = GL_GREEN;
swizzle.b = GL_BLUE;
swizzle.a = GL_ALPHA;
}
void Reset() {
Unbind();
sampler = 0;
}
} texture_units[32];
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} lighting_lut;
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} fog_lut;
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} proctex_noise_lut;
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} proctex_color_map;
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} proctex_alpha_map;
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} proctex_lut;
struct {
GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
} proctex_diff_lut;
struct {
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
@@ -150,7 +159,7 @@ public:
void Apply() const;
/// Resets any references to the given resource
OpenGLState& UnbindTexture(GLuint handle);
OpenGLState& ResetTexture(GLuint handle);
OpenGLState& ResetSampler(GLuint handle);
OpenGLState& ResetProgram(GLuint handle);
OpenGLState& ResetPipeline(GLuint handle);

View File

@@ -100,8 +100,6 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
switch (wrap_mode) {
case Tegra::Texture::WrapMode::Wrap:
return GL_REPEAT;
case Tegra::Texture::WrapMode::Mirror:
return GL_MIRRORED_REPEAT;
case Tegra::Texture::WrapMode::ClampToEdge:
return GL_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::ClampOGL:
@@ -180,75 +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 {};
}
inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
switch (comparison) {
case Maxwell::ComparisonOp::Never:
return GL_NEVER;
case Maxwell::ComparisonOp::Less:
return GL_LESS;
case Maxwell::ComparisonOp::Equal:
return GL_EQUAL;
case Maxwell::ComparisonOp::LessEqual:
return GL_LEQUAL;
case Maxwell::ComparisonOp::Greater:
return GL_GREATER;
case Maxwell::ComparisonOp::NotEqual:
return GL_NOTEQUAL;
case Maxwell::ComparisonOp::GreaterEqual:
return GL_GEQUAL;
case Maxwell::ComparisonOp::Always:
return GL_ALWAYS;
}
NGLOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
UNREACHABLE();
return {};
}
inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
switch (front_face) {
case Maxwell::Cull::FrontFace::ClockWise:
return GL_CW;
case Maxwell::Cull::FrontFace::CounterClockWise:
return GL_CCW;
}
NGLOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
UNREACHABLE();
return {};
}
inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
switch (cull_face) {
case Maxwell::Cull::CullFace::Front:
return GL_FRONT;
case Maxwell::Cull::CullFace::Back:
return GL_BACK;
case Maxwell::Cull::CullFace::FrontAndBack:
return GL_FRONT_AND_BACK;
}
NGLOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
UNREACHABLE();
return {};
}
} // namespace MaxwellToGL

View File

@@ -150,6 +150,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
screen_info)) {
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;
screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes,
Memory::FlushMode::Flush);
@@ -315,7 +316,6 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
}};
state.texture_units[0].texture_2d = screen_info.display_texture;
state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());

View File

@@ -27,7 +27,7 @@ struct TextureInfo {
/// Structure used for storing information about the display target for the Switch screen
struct ScreenInfo {
GLuint display_texture;
const MathUtil::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
MathUtil::Rectangle<float> display_texcoords;
TextureInfo texture;
};

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -5,7 +5,6 @@
#include <cstring>
#include "common/assert.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
@@ -54,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:
@@ -66,18 +63,6 @@ u32 BytesPerPixel(TextureFormat format) {
return 1;
case TextureFormat::R16_G16_B16_A16:
return 8;
case TextureFormat::R32_G32_B32_A32:
return 16;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
}
}
static u32 DepthBytesPerPixel(DepthFormat format) {
switch (format) {
case DepthFormat::Z24_S8_UNORM:
return 4;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
@@ -107,29 +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::R32_G32_B32_A32:
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;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
}
return unswizzled_data;
}
std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
u32 block_height) {
u8* data = Memory::GetPointer(address);
u32 bytes_per_pixel = DepthBytesPerPixel(format);
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
switch (format) {
case DepthFormat::Z24_S8_UNORM:
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
break;
@@ -151,14 +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:
case TextureFormat::R32_G32_B32_A32:
// TODO(Subv): For the time being just forward the same data without any decoding.
rgba_data = texture_data;
break;

View File

@@ -17,12 +17,6 @@ namespace Texture {
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
u32 block_height = TICEntry::DefaultBlockHeight);
/**
* Unswizzles a swizzled depth texture without changing its format.
*/
std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
u32 block_height = TICEntry::DefaultBlockHeight);
/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height);

View File

@@ -122,17 +122,6 @@ enum class ComponentType : u32 {
FLOAT = 7
};
enum class SwizzleSource : u32 {
Zero = 0,
R = 2,
G = 3,
B = 4,
A = 5,
OneInt = 6,
OneFloat = 7,
};
union TextureHandle {
u32 raw;
BitField<0, 20, u32> tic_id;
@@ -150,11 +139,6 @@ struct TICEntry {
BitField<10, 3, ComponentType> g_type;
BitField<13, 3, ComponentType> b_type;
BitField<16, 3, ComponentType> a_type;
BitField<19, 3, SwizzleSource> x_source;
BitField<22, 3, SwizzleSource> y_source;
BitField<25, 3, SwizzleSource> z_source;
BitField<28, 3, SwizzleSource> w_source;
};
u32 address_low;
union {

View File

@@ -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
)

View File

@@ -84,8 +84,6 @@ void Config::ReadValues() {
qt_config->beginGroup("Renderer");
Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool();
Settings::values.use_accurate_framebuffers =
qt_config->value("use_accurate_framebuffers", false).toBool();
Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -186,7 +184,6 @@ void Config::SaveValues() {
qt_config->beginGroup("Renderer");
qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit);
qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers);
// Cast to double because Qt's written float values are not human-readable
qt_config->setValue("bg_red", (double)Settings::values.bg_red);

View File

@@ -59,13 +59,11 @@ void ConfigureGraphics::setConfiguration() {
ui->resolution_factor_combobox->setCurrentIndex(
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit);
ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers);
}
void ConfigureGraphics::applyConfiguration() {
Settings::values.resolution_factor =
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked();
Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked();
Settings::Apply();
}

View File

@@ -29,13 +29,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_accurate_framebuffers">
<property name="text">
<string>Use accurate framebuffers (slow)</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>

View 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();
}

View 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;
};

View 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>

View File

@@ -213,9 +213,6 @@ QString WaitTreeThread::GetText() const {
case THREADSTATUS_WAIT_MUTEX:
status = tr("waiting for mutex");
break;
case THREADSTATUS_WAIT_ARB:
status = tr("waiting for address arbiter");
break;
case THREADSTATUS_DORMANT:
status = tr("dormant");
break;
@@ -243,7 +240,6 @@ QColor WaitTreeThread::GetColor() const {
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_MUTEX:
case THREADSTATUS_WAIT_ARB:
return QColor(Qt::GlobalColor::red);
case THREADSTATUS_DORMANT:
return QColor(Qt::GlobalColor::darkCyan);

View File

@@ -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);
@@ -366,26 +356,13 @@ void GameList::LoadInterfaceLayout() {
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
}
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca"};
const QStringList GameList::supported_file_extensions = {"nso", "nro"};
static bool HasSupportedFileExtension(const std::string& file_name) {
QFileInfo file = QFileInfo(file_name.c_str());
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)),

View File

@@ -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,22 +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_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)
@@ -358,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);
@@ -450,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();
}
@@ -550,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 (*.*)");
@@ -564,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()) {

Some files were not shown because too many files have changed in this diff Show More