Compare commits
6 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c249345c3a | ||
|
|
3e5ffa3e31 | ||
|
|
3ab0a703b5 | ||
|
|
6f35472e41 | ||
|
|
943a895ae2 | ||
|
|
b42faa8f85 |
2
externals/glad/Readme.md
vendored
2
externals/glad/Readme.md
vendored
@@ -1,5 +1,5 @@
|
||||
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
|
||||
|
||||
```
|
||||
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c
|
||||
python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0
|
||||
```
|
||||
|
||||
6
externals/glad/include/KHR/khrplatform.h
vendored
6
externals/glad/include/KHR/khrplatform.h
vendored
@@ -26,7 +26,7 @@
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
|
||||
* $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
@@ -101,8 +101,6 @@
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
@@ -225,7 +223,7 @@ typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
|
||||
13755
externals/glad/include/glad/glad.h
vendored
13755
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
8706
externals/glad/src/glad.c
vendored
8706
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
@@ -37,7 +37,6 @@ namespace Log {
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, HID) \
|
||||
|
||||
@@ -54,7 +54,6 @@ enum class Class : ClassType {
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
Service_APM, ///< The APM (Performance) service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
|
||||
@@ -114,12 +114,6 @@ add_library(core STATIC
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/codecctl.cpp
|
||||
hle/service/audio/codecctl.h
|
||||
hle/service/fatal/fatal.cpp
|
||||
hle/service/fatal/fatal.h
|
||||
hle/service/fatal/fatal_p.cpp
|
||||
hle/service/fatal/fatal_p.h
|
||||
hle/service/fatal/fatal_u.cpp
|
||||
hle/service/fatal/fatal_u.h
|
||||
hle/service/filesystem/filesystem.cpp
|
||||
hle/service/filesystem/filesystem.h
|
||||
hle/service/filesystem/fsp_srv.cpp
|
||||
|
||||
@@ -25,18 +25,22 @@ public:
|
||||
VAddr tls_address;
|
||||
};
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
/**
|
||||
* Runs the CPU for the given number of instructions
|
||||
* @param num_instructions Number of instructions to run
|
||||
*/
|
||||
void Run(int num_instructions) {
|
||||
ExecuteInstructions(num_instructions);
|
||||
this->num_instructions += num_instructions;
|
||||
}
|
||||
|
||||
/// Step CPU by one instruction
|
||||
virtual void Step() = 0;
|
||||
void Step() {
|
||||
Run(1);
|
||||
}
|
||||
|
||||
/// Maps a backing memory region for the CPU
|
||||
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) = 0;
|
||||
|
||||
/// Unmaps a region of memory that was previously mapped using MapBackingMemory
|
||||
virtual void UnmapMemory(VAddr address, size_t size) = 0;
|
||||
Kernel::VMAPermission perms) {}
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
@@ -118,4 +122,19 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// Getter for num_instructions
|
||||
u64 GetNumInstructions() const {
|
||||
return num_instructions;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Executes the given number of instructions
|
||||
* @param num_instructions Number of instructions to executes
|
||||
*/
|
||||
virtual void ExecuteInstructions(int num_instructions) = 0;
|
||||
|
||||
private:
|
||||
u64 num_instructions = 0; ///< Number of instructions executed
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -107,7 +106,7 @@ public:
|
||||
};
|
||||
|
||||
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
|
||||
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
|
||||
const auto page_table = Kernel::g_current_process->vm_manager.page_table.pointers.data();
|
||||
|
||||
Dynarmic::A64::UserConfig config;
|
||||
config.callbacks = cb.get();
|
||||
@@ -122,22 +121,11 @@ std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_C
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Run() {
|
||||
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
|
||||
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic()
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
inner_unicorn.SaveContext(ctx);
|
||||
LoadContext(ctx);
|
||||
PageTableChanged();
|
||||
}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
@@ -147,10 +135,6 @@ void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory,
|
||||
inner_unicorn.MapBackingMemory(address, size, memory, perms);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
|
||||
inner_unicorn.UnmapMemory(address, size);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetPC(u64 pc) {
|
||||
jit->SetPC(pc);
|
||||
}
|
||||
@@ -200,6 +184,13 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
|
||||
cb->tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
|
||||
cb->ticks_remaining = num_instructions;
|
||||
jit->Run();
|
||||
CoreTiming::AddTicks(num_instructions - cb->num_interpreted_instructions);
|
||||
cb->num_interpreted_instructions = 0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
@@ -232,5 +223,4 @@ void ARM_Dynarmic::ClearInstructionCache() {
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
jit = MakeJit(cb);
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
void UnmapMemory(u64 address, size_t size) override;
|
||||
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
@@ -29,8 +29,6 @@ public:
|
||||
u32 GetVFPReg(int index) const override;
|
||||
void SetVFPReg(int index, u32 value) override;
|
||||
u32 GetCPSR() const override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
@@ -39,6 +37,7 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override;
|
||||
@@ -48,6 +47,4 @@ private:
|
||||
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -53,7 +52,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
void* user_data) {
|
||||
ARM_Interface::ThreadContext ctx{};
|
||||
Core::CPU().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%lx, pc=0x%lx, lr=0x%lx", addr,
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx, pc=0x%llx, lr=0x%llx", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
return {};
|
||||
}
|
||||
@@ -78,10 +77,6 @@ void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
|
||||
CHECKED(uc_mem_unmap(uc, address, size));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPC(u64 pc) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
|
||||
}
|
||||
@@ -154,14 +149,6 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
|
||||
@@ -14,7 +14,6 @@ public:
|
||||
~ARM_Unicorn();
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
void UnmapMemory(VAddr address, size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
@@ -30,9 +29,7 @@ public:
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override{};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Core {
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
if (!cpu_core) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
@@ -40,7 +40,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
tight_loop = false;
|
||||
tight_loop = 1;
|
||||
} else {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
@@ -56,11 +56,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
CoreTiming::Advance();
|
||||
if (tight_loop) {
|
||||
cpu_core->Run();
|
||||
} else {
|
||||
cpu_core->Step();
|
||||
}
|
||||
cpu_core->Run(tight_loop);
|
||||
}
|
||||
|
||||
HW::Update();
|
||||
@@ -70,7 +66,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
}
|
||||
|
||||
System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(false);
|
||||
return RunLoop(1);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
|
||||
@@ -99,15 +95,14 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
|
||||
|
||||
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!",
|
||||
static_cast<int>(init_result));
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
|
||||
System::Shutdown();
|
||||
return init_result;
|
||||
}
|
||||
|
||||
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
|
||||
const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", static_cast<int>(load_result));
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
|
||||
System::Shutdown();
|
||||
|
||||
switch (load_result) {
|
||||
@@ -146,8 +141,6 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
|
||||
CoreTiming::Init();
|
||||
|
||||
current_process = Kernel::Process::Create("main");
|
||||
|
||||
switch (Settings::values.cpu_core) {
|
||||
case Settings::CpuCore::Unicorn:
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
@@ -53,10 +52,10 @@ public:
|
||||
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
|
||||
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
|
||||
* update is requested (e.g. on a thread switch).
|
||||
* @param tight_loop If false, the CPU single-steps.
|
||||
* @param tight_loop Number of instructions to execute.
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
ResultStatus RunLoop(bool tight_loop = true);
|
||||
ResultStatus RunLoop(int tight_loop = 100000);
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
@@ -113,10 +112,6 @@ public:
|
||||
return *scheduler;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
|
||||
return current_process;
|
||||
}
|
||||
|
||||
PerfStats perf_stats;
|
||||
FrameLimiter frame_limiter;
|
||||
|
||||
@@ -154,8 +149,6 @@ private:
|
||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Process> current_process;
|
||||
|
||||
/// When true, signals that a reschedule should happen
|
||||
bool reschedule_pending{};
|
||||
|
||||
@@ -176,8 +169,4 @@ inline TelemetrySession& Telemetry() {
|
||||
return System::GetInstance().TelemetrySession();
|
||||
}
|
||||
|
||||
inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
|
||||
return System::GetInstance().CurrentProcess();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/disk_filesystem.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -47,7 +46,7 @@ ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) c
|
||||
}
|
||||
|
||||
std::string SaveData_Factory::GetFullPath() const {
|
||||
u64 title_id = Core::CurrentProcess()->program_id;
|
||||
u64 title_id = Kernel::g_current_process->program_id;
|
||||
// TODO(Subv): Somehow obtain this value.
|
||||
u32 user = 0;
|
||||
return Common::StringFromFormat("%ssave/%016" PRIX64 "/%08X/", nand_directory.c_str(), title_id,
|
||||
|
||||
@@ -693,7 +693,7 @@ static void ReadMemory() {
|
||||
u64 len =
|
||||
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016lx len: %016lx\n", addr, len);
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016llx len: %016llx\n", addr, len);
|
||||
|
||||
if (len * 2 > sizeof(reply)) {
|
||||
SendReply("E01");
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace IPC {
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
|
||||
/// Maximum number of static buffers per thread
|
||||
constexpr size_t MAX_STATIC_BUFFERS = 16;
|
||||
// These errors are commonly returned by invalid IPC translations, so alias them here for
|
||||
// convenience.
|
||||
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -78,7 +77,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
if (handle == CurrentThread) {
|
||||
return GetCurrentThread();
|
||||
} else if (handle == CurrentProcess) {
|
||||
return Core::CurrentProcess();
|
||||
return g_current_process;
|
||||
}
|
||||
|
||||
if (!IsValid(handle)) {
|
||||
|
||||
@@ -27,18 +27,34 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
|
||||
boost::range::remove_erase(connected_sessions, server_session);
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<ServerSession> server_session)
|
||||
: server_session(std::move(server_session)) {
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
|
||||
HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback) {
|
||||
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->wakeup_callback =
|
||||
[context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
|
||||
[context = this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
|
||||
ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
|
||||
callback(thread, context, reason);
|
||||
context.WriteToOutgoingCommandBuffer(*thread);
|
||||
return true;
|
||||
callback(thread, *context, reason);
|
||||
|
||||
auto& process = thread->owner_process;
|
||||
// We must copy the entire command buffer *plus* the entire static buffers area, since
|
||||
// the translation might need to read from it in order to retrieve the StaticBuffer
|
||||
// target addresses.
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
|
||||
Memory::ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
|
||||
cmd_buff.size() * sizeof(u32));
|
||||
context->WriteToOutgoingCommandBuffer(cmd_buff.data(), *process, Kernel::g_handle_table);
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
Memory::WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
|
||||
cmd_buff.size() * sizeof(u32));
|
||||
};
|
||||
|
||||
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
@@ -46,23 +62,15 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
thread->wait_objects = {event};
|
||||
event->AddWaitingThread(thread);
|
||||
|
||||
if (timeout > 0) {
|
||||
if (timeout > 0)
|
||||
thread->WakeAfterDelay(timeout);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
|
||||
: server_session(std::move(server_session)) {
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
|
||||
HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
@@ -72,7 +80,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// If handle descriptor is present, add size of it
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
handle_descriptor_header =
|
||||
std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
if (handle_descriptor_header->send_current_pid) {
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
@@ -115,7 +123,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
domain_message_header =
|
||||
std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
|
||||
std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
|
||||
} else {
|
||||
if (Session()->IsDomain())
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
@@ -123,7 +131,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
|
||||
data_payload_header =
|
||||
std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
|
||||
data_payload_offset = rp.GetCurrentOffset();
|
||||
|
||||
@@ -186,11 +194,8 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
|
||||
HandleTable& dst_table) {
|
||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||
// the integrity and then copy it over to the target command buffer.
|
||||
ParseCommandBuffer(cmd_buf.data(), false);
|
||||
@@ -201,7 +206,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
if (domain_message_header)
|
||||
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
|
||||
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
|
||||
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
|
||||
@@ -243,11 +248,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ std::vector<u8> HLERequestContext::ReadBuffer() const {
|
||||
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
|
||||
|
||||
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %lx is too big", size);
|
||||
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size);
|
||||
|
||||
if (is_buffer_b) {
|
||||
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
}
|
||||
@@ -26,6 +25,7 @@ class Domain;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class Thread;
|
||||
class Event;
|
||||
|
||||
/**
|
||||
@@ -101,14 +101,13 @@ public:
|
||||
* Returns the session through which this request was made. This can be used as a map key to
|
||||
* access per-client data on services.
|
||||
*/
|
||||
const SharedPtr<Kernel::ServerSession>& Session() const {
|
||||
SharedPtr<Kernel::ServerSession> Session() const {
|
||||
return server_session;
|
||||
}
|
||||
|
||||
using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
|
||||
ThreadWakeupReason reason)>;
|
||||
|
||||
/**
|
||||
/*
|
||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
||||
* specified timeout expires.
|
||||
* @param thread Thread to be put to sleep.
|
||||
@@ -122,14 +121,14 @@ public:
|
||||
*/
|
||||
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback);
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
|
||||
HandleTable& dst_table);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
@@ -159,7 +158,7 @@ public:
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
return domain_message_header;
|
||||
}
|
||||
|
||||
@@ -232,10 +231,10 @@ private:
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
|
||||
|
||||
std::shared_ptr<IPC::CommandHeader> command_header;
|
||||
std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::unique_ptr<IPC::CommandHeader> command_header;
|
||||
std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::unique_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::unique_ptr<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
|
||||
|
||||
@@ -41,6 +41,7 @@ void Shutdown() {
|
||||
g_object_address_table.Clear();
|
||||
|
||||
Kernel::ThreadingShutdown();
|
||||
g_current_process = nullptr;
|
||||
|
||||
Kernel::TimersShutdown();
|
||||
Kernel::ResourceLimitsShutdown();
|
||||
|
||||
@@ -33,6 +33,10 @@ enum class HandleType : u32 {
|
||||
ServerSession,
|
||||
};
|
||||
|
||||
enum {
|
||||
DEFAULT_STACK_SIZE = 0x10000,
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
OneShot,
|
||||
Sticky,
|
||||
|
||||
@@ -10,12 +10,12 @@ namespace Kernel {
|
||||
ObjectAddressTable g_object_address_table;
|
||||
|
||||
void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
|
||||
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%lx", addr);
|
||||
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%llx", addr);
|
||||
objects[addr] = obj;
|
||||
}
|
||||
|
||||
void ObjectAddressTable::Close(VAddr addr) {
|
||||
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%lx", addr);
|
||||
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%llx", addr);
|
||||
objects.erase(addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,14 +31,14 @@ CodeSet::~CodeSet() {}
|
||||
|
||||
u32 Process::next_process_id;
|
||||
|
||||
SharedPtr<Process> Process::Create(std::string&& name) {
|
||||
SharedPtr<Process> Process::Create(std::string&& name, u64 program_id) {
|
||||
SharedPtr<Process> process(new Process);
|
||||
|
||||
process->name = std::move(name);
|
||||
process->flags.raw = 0;
|
||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->program_id = program_id;
|
||||
|
||||
process_list.push_back(process);
|
||||
return process;
|
||||
@@ -117,12 +117,11 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
|
||||
}
|
||||
|
||||
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
// Allocate and map the main thread stack
|
||||
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
|
||||
// of the user address space.
|
||||
// Allocate and map stack
|
||||
vm_manager
|
||||
.MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0,
|
||||
stack_size, MemoryState::Mapped)
|
||||
.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
|
||||
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
|
||||
MemoryState::Heap)
|
||||
.Unwrap();
|
||||
misc_memory_used += stack_size;
|
||||
memory_region->used += stack_size;
|
||||
@@ -154,9 +153,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
|
||||
};
|
||||
|
||||
// Map CodeSet segments
|
||||
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
|
||||
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
|
||||
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
|
||||
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code);
|
||||
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static);
|
||||
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static);
|
||||
}
|
||||
|
||||
VAddr Process::GetLinearHeapAreaAddress() const {
|
||||
@@ -183,8 +182,6 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
@@ -204,7 +201,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
|
||||
size, MemoryState::Heap));
|
||||
vm_manager.Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
heap_used += size;
|
||||
memory_region->used += size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
@@ -291,7 +288,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
MemoryState::Mapped));
|
||||
vma->second.meminfo_state));
|
||||
// Protect mirror with permissions from old region
|
||||
vm_manager.Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
@@ -322,4 +319,5 @@ SharedPtr<Process> GetProcessById(u32 process_id) {
|
||||
return *itr;
|
||||
}
|
||||
|
||||
SharedPtr<Process> g_current_process;
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -95,7 +95,7 @@ private:
|
||||
|
||||
class Process final : public Object {
|
||||
public:
|
||||
static SharedPtr<Process> Create(std::string&& name);
|
||||
static SharedPtr<Process> Create(std::string&& name, u64 program_id);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Process";
|
||||
@@ -203,4 +203,5 @@ void ClearProcessList();
|
||||
/// Retrieves a process from the current list of processes.
|
||||
SharedPtr<Process> GetProcessById(u32 process_id);
|
||||
|
||||
extern SharedPtr<Process> g_current_process;
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
@@ -68,7 +67,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
new_thread->CancelWakeupTimer();
|
||||
|
||||
auto previous_process = Core::CurrentProcess();
|
||||
auto previous_process = Kernel::g_current_process;
|
||||
|
||||
current_thread = new_thread;
|
||||
|
||||
@@ -76,8 +75,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
new_thread->status = THREADSTATUS_RUNNING;
|
||||
|
||||
if (previous_process != current_thread->owner_process) {
|
||||
Core::CurrentProcess() = current_thread->owner_process;
|
||||
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
|
||||
Kernel::g_current_process = current_thread->owner_process;
|
||||
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
|
||||
}
|
||||
|
||||
cpu_core->LoadContext(new_thread->context);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
@@ -78,8 +77,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CRITICAL(IPC, "Unknown domain command=%d",
|
||||
static_cast<int>(domain_message_header->command.Value()));
|
||||
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
@@ -93,7 +91,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
|
||||
Kernel::HLERequestContext context(this);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
|
||||
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
|
||||
Kernel::g_handle_table);
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
@@ -52,8 +51,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
|
||||
}
|
||||
|
||||
// Refresh the address mappings for the current process.
|
||||
if (Core::CurrentProcess() != nullptr) {
|
||||
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
|
||||
if (Kernel::g_current_process != nullptr) {
|
||||
Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
|
||||
}
|
||||
} else {
|
||||
auto& vm_manager = shared_memory->owner_process->vm_manager;
|
||||
@@ -107,7 +106,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
|
||||
// Error out if the requested permissions don't match what the creator process allows.
|
||||
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
@@ -115,7 +114,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
// Error out if the provided permissions are not compatible with what the creator process needs.
|
||||
if (other_permissions != MemoryPermission::DontCare &&
|
||||
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ERR_WRONG_PERMISSION;
|
||||
}
|
||||
@@ -126,7 +125,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
if (address != 0) {
|
||||
// TODO(shinyquagsire23): Check for virtual/mappable memory here too?
|
||||
if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, invalid address",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
@@ -143,9 +142,10 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
auto result = target_process->vm_manager.MapMemoryBlock(
|
||||
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
|
||||
if (result.Failed()) {
|
||||
LOG_ERROR(Kernel,
|
||||
"cannot map id=%u, target_address=0x%lx name=%s, error mapping to virtual memory",
|
||||
GetObjectId(), target_address, name.c_str());
|
||||
LOG_ERROR(
|
||||
Kernel,
|
||||
"cannot map id=%u, target_address=0x%llx name=%s, error mapping to virtual memory",
|
||||
GetObjectId(), target_address, name.c_str());
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
@@ -32,14 +31,14 @@ namespace Kernel {
|
||||
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
||||
static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x%llx", heap_size);
|
||||
auto& process = *Core::CurrentProcess();
|
||||
auto& process = *g_current_process;
|
||||
CASCADE_RESULT(*heap_addr,
|
||||
process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%lx", addr);
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%llx", addr);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -47,14 +46,14 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state
|
||||
static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr,
|
||||
src_addr, size);
|
||||
return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
|
||||
return g_current_process->MirrorMemory(dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
/// Unmaps a region that was previously mapped with svcMapMemory
|
||||
static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr,
|
||||
src_addr, size);
|
||||
return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
|
||||
return g_current_process->UnmapMemory(dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
@@ -307,23 +306,23 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
LOG_TRACE(Kernel_SVC, "called info_id=0x%X, info_sub_id=0x%X, handle=0x%08X", info_id,
|
||||
info_sub_id, handle);
|
||||
|
||||
auto& vm_manager = Core::CurrentProcess()->vm_manager;
|
||||
auto& vm_manager = g_current_process->vm_manager;
|
||||
|
||||
switch (static_cast<GetInfoType>(info_id)) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
*result = Core::CurrentProcess()->allowed_processor_mask;
|
||||
*result = g_current_process->allowed_processor_mask;
|
||||
break;
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
*result = Core::CurrentProcess()->allowed_thread_priority_mask;
|
||||
*result = g_current_process->allowed_thread_priority_mask;
|
||||
break;
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
*result = Memory::MAP_REGION_VADDR;
|
||||
*result = vm_manager.GetMapRegionBaseAddr();
|
||||
break;
|
||||
case GetInfoType::MapRegionSize:
|
||||
*result = Memory::MAP_REGION_SIZE;
|
||||
*result = vm_manager.GetAddressSpaceSize();
|
||||
break;
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
*result = Memory::HEAP_VADDR;
|
||||
*result = vm_manager.GetNewMapRegionBaseAddr() + vm_manager.GetNewMapRegionSize();
|
||||
break;
|
||||
case GetInfoType::HeapRegionSize:
|
||||
*result = Memory::HEAP_SIZE;
|
||||
@@ -347,13 +346,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = vm_manager.GetAddressSpaceSize();
|
||||
break;
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
*result = Memory::NEW_MAP_REGION_VADDR;
|
||||
*result = vm_manager.GetNewMapRegionBaseAddr();
|
||||
break;
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
*result = Memory::NEW_MAP_REGION_SIZE;
|
||||
*result = vm_manager.GetNewMapRegionSize();
|
||||
break;
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
|
||||
*result = g_current_process->is_virtual_address_memory_enabled;
|
||||
break;
|
||||
case GetInfoType::TitleId:
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0");
|
||||
@@ -393,7 +392,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
|
||||
// Note: The kernel uses the current process's resource limit instead of
|
||||
// the one from the thread owner's resource limit.
|
||||
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
|
||||
SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit;
|
||||
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
@@ -436,7 +435,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
case MemoryPermission::WriteExecute:
|
||||
case MemoryPermission::ReadWriteExecute:
|
||||
case MemoryPermission::DontCare:
|
||||
return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
|
||||
return shared_memory->Map(g_current_process.get(), addr, permissions_type,
|
||||
MemoryPermission::DontCare);
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
|
||||
@@ -452,7 +451,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
|
||||
|
||||
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
|
||||
|
||||
return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
|
||||
return shared_memory->Unmap(g_current_process.get(), addr);
|
||||
}
|
||||
|
||||
/// Query process memory
|
||||
@@ -464,11 +463,11 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
|
||||
}
|
||||
auto vma = process->vm_manager.FindVMA(addr);
|
||||
memory_info->attributes = 0;
|
||||
if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) {
|
||||
if (vma == g_current_process->vm_manager.vma_map.end()) {
|
||||
memory_info->base_address = 0;
|
||||
memory_info->permission = static_cast<u32>(VMAPermission::None);
|
||||
memory_info->size = 0;
|
||||
memory_info->type = static_cast<u32>(MemoryState::Unmapped);
|
||||
memory_info->type = static_cast<u32>(MemoryState::Free);
|
||||
} else {
|
||||
memory_info->base_address = vma->second.base;
|
||||
memory_info->permission = static_cast<u32>(vma->second.permissions);
|
||||
@@ -488,17 +487,16 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
|
||||
|
||||
/// Exits the current process
|
||||
static void ExitProcess() {
|
||||
LOG_INFO(Kernel_SVC, "Process %u exiting", Core::CurrentProcess()->process_id);
|
||||
LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id);
|
||||
|
||||
ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
|
||||
"Process has already exited");
|
||||
ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited");
|
||||
|
||||
Core::CurrentProcess()->status = ProcessStatus::Exited;
|
||||
g_current_process->status = ProcessStatus::Exited;
|
||||
|
||||
// Stop all the process threads that are currently waiting for objects.
|
||||
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->owner_process != Core::CurrentProcess())
|
||||
if (thread->owner_process != g_current_process)
|
||||
continue;
|
||||
|
||||
if (thread == GetCurrentThread())
|
||||
@@ -527,14 +525,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
|
||||
SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit;
|
||||
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
processor_id = Core::CurrentProcess()->ideal_processor;
|
||||
processor_id = g_current_process->ideal_processor;
|
||||
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -556,7 +554,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
|
||||
CASCADE_RESULT(SharedPtr<Thread> thread,
|
||||
Thread::Create(name, entry_point, priority, arg, processor_id, stack_top,
|
||||
Core::CurrentProcess()));
|
||||
g_current_process));
|
||||
CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread));
|
||||
*out_handle = thread->guest_handle;
|
||||
|
||||
@@ -750,7 +748,7 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
|
||||
/// Creates a TransferMemory object
|
||||
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%lx, size=0x%lx, perms=%08X", addr, size,
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%llx, size=0x%llx, perms=%08X", addr, size,
|
||||
permissions);
|
||||
*handle = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
@@ -55,6 +55,16 @@ inline static u32 const NewThreadId() {
|
||||
Thread::Thread() {}
|
||||
Thread::~Thread() {}
|
||||
|
||||
/**
|
||||
* Check if the specified thread is waiting on the specified address to be arbitrated
|
||||
* @param thread The thread to test
|
||||
* @param wait_address The address to test against
|
||||
* @return True if the thread is waiting, false otherwise
|
||||
*/
|
||||
static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
|
||||
return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
|
||||
}
|
||||
|
||||
void Thread::Stop() {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
|
||||
@@ -84,7 +94,7 @@ void Thread::Stop() {
|
||||
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
|
||||
u64 tls_slot =
|
||||
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
|
||||
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
|
||||
Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
|
||||
}
|
||||
|
||||
void WaitCurrentThread_Sleep() {
|
||||
@@ -92,6 +102,12 @@ void WaitCurrentThread_Sleep() {
|
||||
thread->status = THREADSTATUS_WAIT_SLEEP;
|
||||
}
|
||||
|
||||
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->wait_address = wait_address;
|
||||
thread->status = THREADSTATUS_WAIT_ARB;
|
||||
}
|
||||
|
||||
void ExitCurrentThread() {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->Stop();
|
||||
@@ -113,7 +129,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
|
||||
bool resume = true;
|
||||
|
||||
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB ||
|
||||
thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
|
||||
|
||||
// Remove the thread from each of its waiting objects' waitlists
|
||||
@@ -148,7 +164,7 @@ void Thread::ResumeFromWait() {
|
||||
switch (status) {
|
||||
case THREADSTATUS_WAIT_SYNCH_ALL:
|
||||
case THREADSTATUS_WAIT_SYNCH_ANY:
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
case THREADSTATUS_WAIT_IPC:
|
||||
break;
|
||||
@@ -299,7 +315,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
// TODO(Subv): Find the correct MemoryState for this region.
|
||||
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
|
||||
linheap_memory, offset, Memory::PAGE_SIZE,
|
||||
MemoryState::ThreadLocal);
|
||||
MemoryState::ThreadLocalStorage);
|
||||
}
|
||||
|
||||
// Mark the slot as used
|
||||
@@ -338,11 +354,11 @@ void Thread::BoostPriority(u32 priority) {
|
||||
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
|
||||
SharedPtr<Process> owner_process) {
|
||||
// Setup page table so we can write to memory
|
||||
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
|
||||
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
|
||||
Memory::STACK_VADDR_END, owner_process);
|
||||
Memory::HEAP_VADDR_END, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ enum ThreadProcessorId : s32 {
|
||||
enum ThreadStatus {
|
||||
THREADSTATUS_RUNNING, ///< Currently running
|
||||
THREADSTATUS_READY, ///< Ready to run
|
||||
THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
|
||||
THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
|
||||
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
|
||||
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
|
||||
|
||||
@@ -18,26 +18,8 @@ namespace Kernel {
|
||||
|
||||
static const char* GetMemoryStateName(MemoryState state) {
|
||||
static const char* names[] = {
|
||||
"Unmapped",
|
||||
"Io",
|
||||
"Normal",
|
||||
"CodeStatic",
|
||||
"CodeMutable",
|
||||
"Heap",
|
||||
"Shared",
|
||||
"Unknown1"
|
||||
"ModuleCodeStatic",
|
||||
"ModuleCodeMutable",
|
||||
"IpcBuffer0",
|
||||
"Mapped",
|
||||
"ThreadLocal",
|
||||
"TransferMemoryIsolated",
|
||||
"TransferMemory",
|
||||
"ProcessMemory",
|
||||
"Unknown2"
|
||||
"IpcBuffer1",
|
||||
"IpcBuffer3",
|
||||
"KernelStack",
|
||||
"Free", "Reserved", "IO", "Static", "Code", "Private",
|
||||
"Shared", "Continuous", "Aliased", "Alias", "AliasCode", "Locked",
|
||||
};
|
||||
|
||||
return names[(int)state];
|
||||
@@ -160,7 +142,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
||||
VirtualMemoryArea& vma = vma_handle->second;
|
||||
vma.type = VMAType::Free;
|
||||
vma.permissions = VMAPermission::None;
|
||||
vma.meminfo_state = MemoryState::Unmapped;
|
||||
vma.meminfo_state = MemoryState::Free;
|
||||
|
||||
vma.backing_block = nullptr;
|
||||
vma.offset = 0;
|
||||
@@ -184,9 +166,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
|
||||
}
|
||||
|
||||
ASSERT(FindVMA(target)->second.size >= size);
|
||||
|
||||
Core::CPU().UnmapMemory(target, size);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -398,4 +377,19 @@ u64 VMManager::GetAddressSpaceSize() {
|
||||
return MAX_ADDRESS;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetMapRegionBaseAddr() {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return Memory::HEAP_VADDR;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetNewMapRegionBaseAddr() {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x8000000;
|
||||
}
|
||||
|
||||
u64 VMManager::GetNewMapRegionSize() {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x8000000;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -41,24 +41,15 @@ enum class VMAPermission : u8 {
|
||||
|
||||
/// Set of values returned in MemoryInfo.state by svcQueryMemory.
|
||||
enum class MemoryState : u32 {
|
||||
Unmapped = 0x0,
|
||||
Io = 0x1,
|
||||
Normal = 0x2,
|
||||
CodeStatic = 0x3,
|
||||
CodeMutable = 0x4,
|
||||
Heap = 0x5,
|
||||
Shared = 0x6,
|
||||
ModuleCodeStatic = 0x8,
|
||||
ModuleCodeMutable = 0x9,
|
||||
IpcBuffer0 = 0xA,
|
||||
Mapped = 0xB,
|
||||
ThreadLocal = 0xC,
|
||||
TransferMemoryIsolated = 0xD,
|
||||
TransferMemory = 0xE,
|
||||
ProcessMemory = 0xF,
|
||||
IpcBuffer1 = 0x11,
|
||||
IpcBuffer3 = 0x12,
|
||||
KernelStack = 0x13,
|
||||
Free = 0,
|
||||
IO = 1,
|
||||
Normal = 2,
|
||||
Code = 3,
|
||||
Static = 4,
|
||||
Heap = 5,
|
||||
Shared = 6,
|
||||
Mapped = 6,
|
||||
ThreadLocalStorage = 12,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -75,7 +66,7 @@ struct VirtualMemoryArea {
|
||||
VMAType type = VMAType::Free;
|
||||
VMAPermission permissions = VMAPermission::None;
|
||||
/// Tag returned by svcQueryMemory. Not otherwise used.
|
||||
MemoryState meminfo_state = MemoryState::Unmapped;
|
||||
MemoryState meminfo_state = MemoryState::Free;
|
||||
|
||||
// Settings for type = AllocatedMemoryBlock
|
||||
/// Memory block backing this VMA.
|
||||
@@ -201,6 +192,15 @@ public:
|
||||
/// Gets the total address space address size, used by svcGetInfo
|
||||
u64 GetAddressSpaceSize();
|
||||
|
||||
/// Gets the map region base address, used by svcGetInfo
|
||||
VAddr GetMapRegionBaseAddr();
|
||||
|
||||
/// Gets the base address for a new memory region, used by svcGetInfo
|
||||
VAddr GetNewMapRegionBaseAddr();
|
||||
|
||||
/// Gets the size for a new memory region, used by svcGetInfo
|
||||
u64 GetNewMapRegionSize();
|
||||
|
||||
/// Each VMManager has its own page table, which is set as the main one when the owning process
|
||||
/// is scheduled.
|
||||
Memory::PageTable page_table;
|
||||
|
||||
@@ -39,8 +39,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
||||
for (const auto& thread : waiting_threads) {
|
||||
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
||||
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
|
||||
thread->status == THREADSTATUS_WAIT_HLE_EVENT,
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
|
||||
"Inconsistent thread statuses in waiting_threads");
|
||||
|
||||
if (thread->current_priority >= candidate_priority)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
|
||||
void Module::Interface::FatalSimple(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 error_code = rp.Pop<u32>();
|
||||
LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x%X", error_code);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::TransitionToFatalError(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Fatal, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<Fatal_P>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<Fatal_U>(module)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -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 {
|
||||
namespace Fatal {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void FatalSimple(Kernel::HLERequestContext& ctx);
|
||||
void TransitionToFatalError(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
Fatal_P::Fatal_P(std::shared_ptr<Module> module)
|
||||
: Module::Interface(std::move(module), "fatal:p") {}
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -1,18 +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/fatal/fatal.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
class Fatal_P final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_P(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &Fatal_U::FatalSimple, "FatalSimple"},
|
||||
{2, &Fatal_U::TransitionToFatalError, "TransitionToFatalError"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -1,18 +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/fatal/fatal.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
class Fatal_U final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_U(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -35,7 +35,7 @@ private:
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -123,7 +123,7 @@ private:
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
{0, &IRequest::GetRequestState, "GetRequestState"},
|
||||
{1, &IRequest::GetResult, "GetResult"},
|
||||
{2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
|
||||
{3, &IRequest::Cancel, "Cancel"},
|
||||
{3, nullptr, "Cancel"},
|
||||
{4, nullptr, "Submit"},
|
||||
{5, nullptr, "SetRequirement"},
|
||||
{6, nullptr, "SetRequirementPreset"},
|
||||
@@ -80,11 +80,6 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event1, event2);
|
||||
}
|
||||
void Cancel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event1, event2;
|
||||
};
|
||||
@@ -101,56 +96,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class IGeneralService final : public ServiceFramework<IGeneralService> {
|
||||
public:
|
||||
IGeneralService();
|
||||
|
||||
private:
|
||||
void GetClientId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IScanRequest>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
void CreateRequest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IRequest>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<INetworkProfile>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IGeneralService::GetClientId, "GetClientId"},
|
||||
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
|
||||
{4, &IGeneralService::CreateRequest, "CreateRequest"},
|
||||
{5, nullptr, "GetCurrentNetworkProfile"},
|
||||
{6, nullptr, "EnumerateNetworkInterfaces"},
|
||||
{7, nullptr, "EnumerateNetworkProfiles"},
|
||||
{6, nullptr, "GetCurrentNetworkProfile"},
|
||||
{7, nullptr, "EnumerateNetworkInterfaces"},
|
||||
{8, nullptr, "GetNetworkProfile"},
|
||||
{9, nullptr, "SetNetworkProfile"},
|
||||
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
|
||||
@@ -185,28 +137,50 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IScanRequest>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
rb.PushIpcInterface<IRequest>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<INetworkProfile>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<NIFM_A>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_S>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_U>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_A>()->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_S>()->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_U>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -9,18 +9,16 @@
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class Module final {
|
||||
class IGeneralService final : public ServiceFramework<IGeneralService> {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
IGeneralService();
|
||||
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
private:
|
||||
void GetClientId(Kernel::HLERequestContext& ctx);
|
||||
void CreateScanRequest(Kernel::HLERequestContext& ctx);
|
||||
void CreateRequest(Kernel::HLERequestContext& ctx);
|
||||
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
|
||||
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
@@ -2,12 +2,29 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_a.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") {
|
||||
void NIFM_A::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void NIFM_A::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
NIFM_A::NIFM_A() : ServiceFramework("nifm:a") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NIFM_A::CreateGeneralService, "CreateGeneralService"},
|
||||
|
||||
@@ -4,14 +4,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class NIFM_A final : public Module::Interface {
|
||||
class NIFM_A final : public ServiceFramework<NIFM_A> {
|
||||
public:
|
||||
explicit NIFM_A(std::shared_ptr<Module> module);
|
||||
NIFM_A();
|
||||
~NIFM_A() = default;
|
||||
|
||||
private:
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -2,12 +2,29 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") {
|
||||
void NIFM_S::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void NIFM_S::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
NIFM_S::NIFM_S() : ServiceFramework("nifm:s") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NIFM_S::CreateGeneralService, "CreateGeneralService"},
|
||||
|
||||
@@ -4,14 +4,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class NIFM_S final : public Module::Interface {
|
||||
class NIFM_S final : public ServiceFramework<NIFM_S> {
|
||||
public:
|
||||
explicit NIFM_S(std::shared_ptr<Module> module);
|
||||
NIFM_S();
|
||||
~NIFM_S() = default;
|
||||
|
||||
private:
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -2,12 +2,29 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") {
|
||||
void NIFM_U::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void NIFM_U::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
NIFM_U::NIFM_U() : ServiceFramework("nifm:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NIFM_U::CreateGeneralService, "CreateGeneralService"},
|
||||
|
||||
@@ -4,14 +4,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class NIFM_U final : public Module::Interface {
|
||||
class NIFM_U final : public ServiceFramework<NIFM_U> {
|
||||
public:
|
||||
explicit NIFM_U(std::shared_ptr<Module> module);
|
||||
NIFM_U();
|
||||
~NIFM_U() = default;
|
||||
|
||||
private:
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/ns/pl_u.h"
|
||||
|
||||
@@ -91,13 +90,13 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
// dump. In the future, we need to replace this with a more robust solution.
|
||||
|
||||
// Map backing memory for the font data
|
||||
Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0,
|
||||
SHARED_FONT_MEM_SIZE,
|
||||
Kernel::MemoryState::Shared);
|
||||
Kernel::g_current_process->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0,
|
||||
SHARED_FONT_MEM_SIZE,
|
||||
Kernel::MemoryState::Shared);
|
||||
|
||||
// Create shared font memory object
|
||||
shared_font_mem = Kernel::SharedMemory::Create(
|
||||
Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
|
||||
Kernel::g_current_process, SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
|
||||
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
|
||||
"PL_U:shared_font_mem");
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
|
||||
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_WARNING(Service,
|
||||
"Drawing from address %lx offset %08X Width %u Height %u Stride %u Format %u", addr,
|
||||
offset, width, height, stride, format);
|
||||
"Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u",
|
||||
addr, offset, width, height, stride, format);
|
||||
|
||||
using PixelFormat = RendererBase::FramebufferInfo::PixelFormat;
|
||||
using Flags = NVFlinger::BufferQueue::BufferTransformFlags;
|
||||
|
||||
@@ -26,30 +26,26 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
|
||||
// Only consider free buffers. Buffers become free once again after they've been Acquired
|
||||
// and Released by the compositor, see the NVFlinger::Compose method.
|
||||
if (buffer.status != Buffer::Status::Free) {
|
||||
if (buffer.status != Buffer::Status::Free)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that the parameters match.
|
||||
return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height;
|
||||
auto& igbp_buffer = buffer.igbp_buffer;
|
||||
return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
|
||||
igbp_buffer.height == height;
|
||||
});
|
||||
|
||||
if (itr == queue.end()) {
|
||||
return boost::none;
|
||||
LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d",
|
||||
pixel_format, width, height);
|
||||
itr = queue.begin();
|
||||
}
|
||||
|
||||
buffer_wait_event = nullptr;
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
}
|
||||
@@ -87,10 +83,6 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(itr != queue.end());
|
||||
ASSERT(itr->status == Buffer::Status::Acquired);
|
||||
itr->status = Buffer::Status::Free;
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
@@ -106,10 +98,5 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
|
||||
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
|
||||
buffer_wait_event = std::move(wait_event);
|
||||
}
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
||||
|
||||
@@ -69,13 +69,12 @@ public:
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform);
|
||||
boost::optional<const Buffer&> AcquireBuffer();
|
||||
void ReleaseBuffer(u32 slot);
|
||||
u32 Query(QueryType type);
|
||||
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
|
||||
|
||||
u32 GetId() const {
|
||||
return id;
|
||||
@@ -91,9 +90,6 @@ private:
|
||||
|
||||
std::vector<Buffer> queue;
|
||||
Kernel::SharedPtr<Kernel::Event> native_handle;
|
||||
|
||||
/// Used to signal waiting thread when no buffers are available
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
|
||||
};
|
||||
|
||||
} // namespace NVFlinger
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
@@ -20,7 +19,6 @@
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/friend/friend.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
@@ -150,11 +148,19 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("command_type=%d", static_cast<int>(context.GetCommandType()));
|
||||
UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType());
|
||||
}
|
||||
|
||||
context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
|
||||
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
|
||||
auto thread = Kernel::GetCurrentThread();
|
||||
ASSERT(thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_WAIT_HLE_EVENT);
|
||||
// Only write the response immediately if the thread is still running. If the HLE handler put
|
||||
// the thread to sleep then the writing of the command buffer will be deferred to the wakeup
|
||||
// callback.
|
||||
if (thread->status == THREADSTATUS_RUNNING) {
|
||||
context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
|
||||
Kernel::g_handle_table);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -180,7 +186,6 @@ void Init() {
|
||||
AOC::InstallInterfaces(*SM::g_service_manager);
|
||||
APM::InstallInterfaces(*SM::g_service_manager);
|
||||
Audio::InstallInterfaces(*SM::g_service_manager);
|
||||
Fatal::InstallInterfaces(*SM::g_service_manager);
|
||||
FileSystem::InstallInterfaces(*SM::g_service_manager);
|
||||
Friend::InstallInterfaces(*SM::g_service_manager);
|
||||
HID::InstallInterfaces(*SM::g_service_manager);
|
||||
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 posix_time = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016lX", posix_time);
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016llX", posix_time);
|
||||
|
||||
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
@@ -474,7 +471,7 @@ private:
|
||||
u32 flags = rp.Pop<u32>();
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
LOG_DEBUG(Service_VI, "called, transaction=%x", static_cast<u32>(transaction));
|
||||
LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
|
||||
|
||||
if (transaction == TransactionId::Connect) {
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
@@ -489,30 +486,12 @@ private:
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::DequeueBuffer) {
|
||||
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
const u32 width{request.data.width};
|
||||
const u32 height{request.data.height};
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
|
||||
if (slot != boost::none) {
|
||||
// Buffer is available
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else {
|
||||
// Wait the current thread until a buffer becomes available
|
||||
auto wait_event = ctx.SleepClientThread(
|
||||
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
});
|
||||
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
|
||||
}
|
||||
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
|
||||
request.data.height);
|
||||
|
||||
IGBPDequeueBufferResponseParcel response{slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::RequestBuffer) {
|
||||
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
|
||||
@@ -649,152 +628,144 @@ private:
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
public:
|
||||
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~IApplicationDisplayService() = default;
|
||||
void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
private:
|
||||
void GetRelayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemDisplayService>();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemDisplayService>();
|
||||
}
|
||||
void IApplicationDisplayService::GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
|
||||
}
|
||||
void IApplicationDisplayService::GetIndirectDisplayTransactionService(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
void IApplicationDisplayService::OpenDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
void OpenDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
std::string name(name_buf.begin(), end);
|
||||
|
||||
std::string name(name_buf.begin(), end);
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(nv_flinger->OpenDisplay(name));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(nv_flinger->OpenDisplay(name));
|
||||
}
|
||||
void IApplicationDisplayService::CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 scaling_mode = rp.Pop<u32>();
|
||||
u64 unknown = rp.Pop<u64>();
|
||||
std::string display_name(name_buf.begin(), end);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
|
||||
void ListDisplays(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
DisplayInfo display_info;
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(1);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
}
|
||||
u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
void OpenLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
|
||||
std::string display_name(name_buf.begin(), end);
|
||||
void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 flags = rp.Pop<u32>();
|
||||
rp.Pop<u32>(); // padding
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
|
||||
u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(layer_id);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 flags = rp.Pop<u32>();
|
||||
rp.Pop<u32>(); // padding
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
|
||||
u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(layer_id);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 scaling_mode = rp.Pop<u32>();
|
||||
u64 unknown = rp.Pop<u64>();
|
||||
|
||||
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
DisplayInfo display_info;
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(1);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
|
||||
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
|
||||
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(vsync_event);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(vsync_event);
|
||||
}
|
||||
|
||||
IApplicationDisplayService::IApplicationDisplayService(
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
@@ -817,24 +788,11 @@ IApplicationDisplayService::IApplicationDisplayService(
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
|
||||
|
||||
void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -14,19 +17,26 @@ struct EventType;
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
class Module final {
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~IApplicationDisplayService() = default;
|
||||
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
private:
|
||||
void GetRelayService(Kernel::HLERequestContext& ctx);
|
||||
void GetSystemDisplayService(Kernel::HLERequestContext& ctx);
|
||||
void GetManagerDisplayService(Kernel::HLERequestContext& ctx);
|
||||
void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx);
|
||||
void OpenDisplay(Kernel::HLERequestContext& ctx);
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx);
|
||||
void ListDisplays(Kernel::HLERequestContext& ctx);
|
||||
void OpenLayer(Kernel::HLERequestContext& ctx);
|
||||
void CreateStrayLayer(Kernel::HLERequestContext& ctx);
|
||||
void DestroyStrayLayer(Kernel::HLERequestContext& ctx);
|
||||
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
/// Registers all VI services with the specified service manager.
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_m.h"
|
||||
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
|
||||
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("vi:m"), nv_flinger(std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{2, &VI_M::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -4,14 +4,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace VI {
|
||||
|
||||
class VI_M final : public Module::Interface {
|
||||
class VI_M final : public ServiceFramework<VI_M> {
|
||||
public:
|
||||
explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_M() = default;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
|
||||
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("vi:s"), nv_flinger(std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &VI_S::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -4,14 +4,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace VI {
|
||||
|
||||
class VI_S final : public Module::Interface {
|
||||
class VI_S final : public ServiceFramework<VI_S> {
|
||||
public:
|
||||
explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_S() = default;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
|
||||
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("vi:u"), nv_flinger(std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -4,14 +4,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace VI {
|
||||
|
||||
class VI_U final : public Module::Interface {
|
||||
class VI_U final : public ServiceFramework<VI_U> {
|
||||
public:
|
||||
explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_U() = default;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -119,6 +119,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
}
|
||||
metadata.Print();
|
||||
|
||||
process = Kernel::Process::Create("main", metadata.GetTitleID());
|
||||
|
||||
// Load NSO modules
|
||||
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
|
||||
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
|
||||
@@ -133,7 +135,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
}
|
||||
}
|
||||
|
||||
process->program_id = metadata.GetTitleID();
|
||||
process->svc_access_mask.set();
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->resource_limit =
|
||||
|
||||
@@ -406,6 +406,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
|
||||
codeset->name = filename;
|
||||
|
||||
process = Kernel::Process::Create("main", 0);
|
||||
process->LoadModule(codeset, codeset->entrypoint);
|
||||
process->svc_access_mask.set();
|
||||
process->address_mappings = default_address_mappings;
|
||||
@@ -414,7 +415,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
|
||||
process->Run(codeset->entrypoint, 48, Memory::STACK_SIZE);
|
||||
process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
||||
@@ -84,7 +84,7 @@ void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Loader, "Unknown relocation type: %d", static_cast<int>(rela.type));
|
||||
LOG_CRITICAL(Loader, "Unknown relocation type: %d", rela.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/loader/nro.h"
|
||||
@@ -113,7 +112,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
|
||||
// Load codeset for current process
|
||||
codeset->name = path;
|
||||
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
||||
Core::CurrentProcess()->LoadModule(codeset, load_base);
|
||||
Kernel::g_current_process->LoadModule(codeset, load_base);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -126,6 +125,8 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
return ResultStatus::Error;
|
||||
}
|
||||
|
||||
process = Kernel::Process::Create("main", 0);
|
||||
|
||||
// Load NRO
|
||||
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
|
||||
|
||||
@@ -137,7 +138,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
process->Run(base_addr, 48, Memory::STACK_SIZE);
|
||||
process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/loader/nso.h"
|
||||
@@ -143,7 +142,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
|
||||
// Load codeset for current process
|
||||
codeset->name = path;
|
||||
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
||||
Core::CurrentProcess()->LoadModule(codeset, load_base);
|
||||
Kernel::g_current_process->LoadModule(codeset, load_base);
|
||||
|
||||
return load_base + image_size;
|
||||
}
|
||||
@@ -156,6 +155,8 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
return ResultStatus::Error;
|
||||
}
|
||||
|
||||
process = Kernel::Process::Create("main", 0);
|
||||
|
||||
// Load module
|
||||
LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR);
|
||||
LOG_DEBUG(Loader, "loaded module %s @ 0x%" PRIx64, filepath.c_str(),
|
||||
@@ -165,7 +166,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::STACK_SIZE);
|
||||
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
||||
@@ -109,7 +109,7 @@ static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_tabl
|
||||
}
|
||||
|
||||
static std::set<MemoryHookPointer> GetSpecialHandlers(VAddr vaddr, u64 size) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
return GetSpecialHandlers(page_table, vaddr, size);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
|
||||
}
|
||||
|
||||
bool IsValidVirtualAddress(const VAddr vaddr) {
|
||||
return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr);
|
||||
return IsValidVirtualAddress(*Kernel::g_current_process, vaddr);
|
||||
}
|
||||
|
||||
bool IsValidPhysicalAddress(const PAddr paddr) {
|
||||
@@ -364,7 +364,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
|
||||
}
|
||||
|
||||
void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
|
||||
ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size);
|
||||
ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size);
|
||||
}
|
||||
|
||||
void Write8(const VAddr addr, const u8 data) {
|
||||
@@ -435,11 +435,11 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
|
||||
}
|
||||
|
||||
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
|
||||
WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size);
|
||||
WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size);
|
||||
}
|
||||
|
||||
void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||
const auto& process = *Core::CurrentProcess();
|
||||
const auto& process = *Kernel::g_current_process;
|
||||
|
||||
size_t remaining_size = size;
|
||||
size_t page_index = dest_addr >> PAGE_BITS;
|
||||
@@ -480,7 +480,7 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||
}
|
||||
|
||||
void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||
const auto& process = *Core::CurrentProcess();
|
||||
const auto& process = *Kernel::g_current_process;
|
||||
|
||||
size_t remaining_size = size;
|
||||
size_t page_index = src_addr >> PAGE_BITS;
|
||||
@@ -526,7 +526,7 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||
|
||||
template <>
|
||||
boost::optional<u8> ReadSpecial<u8>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8)))
|
||||
if (auto result = handler->Read8(addr))
|
||||
return *result;
|
||||
@@ -535,7 +535,7 @@ boost::optional<u8> ReadSpecial<u8>(VAddr addr) {
|
||||
|
||||
template <>
|
||||
boost::optional<u16> ReadSpecial<u16>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16)))
|
||||
if (auto result = handler->Read16(addr))
|
||||
return *result;
|
||||
@@ -544,7 +544,7 @@ boost::optional<u16> ReadSpecial<u16>(VAddr addr) {
|
||||
|
||||
template <>
|
||||
boost::optional<u32> ReadSpecial<u32>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32)))
|
||||
if (auto result = handler->Read32(addr))
|
||||
return *result;
|
||||
@@ -553,7 +553,7 @@ boost::optional<u32> ReadSpecial<u32>(VAddr addr) {
|
||||
|
||||
template <>
|
||||
boost::optional<u64> ReadSpecial<u64>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64)))
|
||||
if (auto result = handler->Read64(addr))
|
||||
return *result;
|
||||
@@ -562,7 +562,7 @@ boost::optional<u64> ReadSpecial<u64>(VAddr addr) {
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u8>(VAddr addr, const u8 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8)))
|
||||
if (handler->Write8(addr, data))
|
||||
return true;
|
||||
@@ -571,7 +571,7 @@ bool WriteSpecial<u8>(VAddr addr, const u8 data) {
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u16>(VAddr addr, const u16 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16)))
|
||||
if (handler->Write16(addr, data))
|
||||
return true;
|
||||
@@ -580,7 +580,7 @@ bool WriteSpecial<u16>(VAddr addr, const u16 data) {
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u32>(VAddr addr, const u32 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32)))
|
||||
if (handler->Write32(addr, data))
|
||||
return true;
|
||||
@@ -589,7 +589,7 @@ bool WriteSpecial<u32>(VAddr addr, const u32 data) {
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u64>(VAddr addr, const u64 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64)))
|
||||
if (handler->Write64(addr, data))
|
||||
return true;
|
||||
@@ -632,7 +632,7 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
|
||||
} else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
|
||||
return addr - VRAM_PADDR + VRAM_VADDR;
|
||||
} else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
|
||||
return addr - FCRAM_PADDR + Core::CurrentProcess()->GetLinearHeapAreaAddress();
|
||||
return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress();
|
||||
} else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) {
|
||||
return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
|
||||
} else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
|
||||
|
||||
@@ -129,6 +129,21 @@ enum : VAddr {
|
||||
PROCESS_IMAGE_MAX_SIZE = 0x08000000,
|
||||
PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE,
|
||||
|
||||
/// Area where IPC buffers are mapped onto.
|
||||
IPC_MAPPING_VADDR = 0x04000000,
|
||||
IPC_MAPPING_SIZE = 0x04000000,
|
||||
IPC_MAPPING_VADDR_END = IPC_MAPPING_VADDR + IPC_MAPPING_SIZE,
|
||||
|
||||
/// Application heap (includes stack).
|
||||
HEAP_VADDR = 0x108000000,
|
||||
HEAP_SIZE = 0xF0000000,
|
||||
HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
|
||||
|
||||
/// Area where shared memory buffers are mapped onto.
|
||||
SHARED_MEMORY_VADDR = 0x10000000,
|
||||
SHARED_MEMORY_SIZE = 0x04000000,
|
||||
SHARED_MEMORY_VADDR_END = SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE,
|
||||
|
||||
/// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical
|
||||
/// memory.
|
||||
LINEAR_HEAP_VADDR = 0x14000000,
|
||||
@@ -161,39 +176,14 @@ enum : VAddr {
|
||||
SHARED_PAGE_SIZE = 0x00001000,
|
||||
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
|
||||
|
||||
/// Area where TLS (Thread-Local Storage) buffers are allocated.
|
||||
TLS_AREA_VADDR = 0x228000000,
|
||||
TLS_ENTRY_SIZE = 0x200,
|
||||
|
||||
/// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
|
||||
NEW_LINEAR_HEAP_VADDR = 0x30000000,
|
||||
NEW_LINEAR_HEAP_SIZE = 0x10000000,
|
||||
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
|
||||
|
||||
/// Area where TLS (Thread-Local Storage) buffers are allocated.
|
||||
TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END,
|
||||
TLS_ENTRY_SIZE = 0x200,
|
||||
TLS_AREA_SIZE = 0x10000000,
|
||||
TLS_ADREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
|
||||
|
||||
/// Application stack
|
||||
STACK_VADDR = TLS_ADREA_VADDR_END,
|
||||
STACK_SIZE = 0x10000,
|
||||
STACK_VADDR_END = STACK_VADDR + STACK_SIZE,
|
||||
|
||||
/// Application heap
|
||||
/// Size is confirmed to be a static value on fw 3.0.0
|
||||
HEAP_VADDR = 0x108000000,
|
||||
HEAP_SIZE = 0x180000000,
|
||||
HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
|
||||
|
||||
/// New map region
|
||||
/// Size is confirmed to be a static value on fw 3.0.0
|
||||
NEW_MAP_REGION_VADDR = HEAP_VADDR_END,
|
||||
NEW_MAP_REGION_SIZE = 0x80000000,
|
||||
NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE,
|
||||
|
||||
/// Map region
|
||||
/// Size is confirmed to be a static value on fw 3.0.0
|
||||
MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
|
||||
MAP_REGION_SIZE = 0x1000000000,
|
||||
MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
|
||||
};
|
||||
|
||||
/// Currently active page table
|
||||
|
||||
@@ -15,8 +15,8 @@ static Memory::PageTable* page_table = nullptr;
|
||||
TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
|
||||
|
||||
Core::CurrentProcess() = Kernel::Process::Create("");
|
||||
page_table = &Core::CurrentProcess()->vm_manager.page_table;
|
||||
Kernel::g_current_process = Kernel::Process::Create("", 0);
|
||||
page_table = &Kernel::g_current_process->vm_manager.page_table;
|
||||
|
||||
page_table->pointers.fill(nullptr);
|
||||
page_table->special_regions.clear();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory][!hide]") {
|
||||
SECTION("these regions should not be mapped on an empty process") {
|
||||
auto process = Kernel::Process::Create("");
|
||||
auto process = Kernel::Process::Create("", 0);
|
||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
|
||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false);
|
||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false);
|
||||
@@ -20,14 +20,14 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory][!hide]") {
|
||||
}
|
||||
|
||||
SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") {
|
||||
auto process = Kernel::Process::Create("");
|
||||
auto process = Kernel::Process::Create("", 0);
|
||||
Kernel::MapSharedPages(process->vm_manager);
|
||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true);
|
||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true);
|
||||
}
|
||||
|
||||
SECTION("special regions should be valid after mapping them") {
|
||||
auto process = Kernel::Process::Create("");
|
||||
auto process = Kernel::Process::Create("", 0);
|
||||
SECTION("VRAM") {
|
||||
Kernel::HandleSpecialMapping(process->vm_manager,
|
||||
{Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false});
|
||||
@@ -48,7 +48,7 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory][!hide]") {
|
||||
}
|
||||
|
||||
SECTION("Unmapping a VAddr should make it invalid") {
|
||||
auto process = Kernel::Process::Create("");
|
||||
auto process = Kernel::Process::Create("", 0);
|
||||
Kernel::MapSharedPages(process->vm_manager);
|
||||
process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE);
|
||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false);
|
||||
|
||||
@@ -7,28 +7,16 @@ add_library(video_core STATIC
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
memory_manager.cpp
|
||||
memory_manager.h
|
||||
rasterizer_interface.h
|
||||
renderer_base.cpp
|
||||
renderer_base.h
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
renderer_opengl/gl_rasterizer_cache.h
|
||||
renderer_opengl/gl_resource_manager.h
|
||||
renderer_opengl/gl_shader_decompiler.cpp
|
||||
renderer_opengl/gl_shader_decompiler.h
|
||||
renderer_opengl/gl_shader_gen.cpp
|
||||
renderer_opengl/gl_shader_gen.h
|
||||
renderer_opengl/gl_shader_util.cpp
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_state.cpp
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/gl_stream_buffer.cpp
|
||||
renderer_opengl/gl_stream_buffer.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
utils.h
|
||||
|
||||
@@ -24,37 +24,12 @@ namespace Tegra {
|
||||
|
||||
enum class BufferMethods {
|
||||
BindObject = 0,
|
||||
SetGraphMacroCode = 0x45,
|
||||
SetGraphMacroCodeArg = 0x46,
|
||||
SetGraphMacroEntry = 0x47,
|
||||
CountBufferMethods = 0x100,
|
||||
};
|
||||
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X remaining params %u",
|
||||
method, subchannel, value, remaining_params);
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::SetGraphMacroEntry)) {
|
||||
// Prepare to upload a new macro, reset the upload counter.
|
||||
LOG_DEBUG(HW_GPU, "Uploading GPU macro %08X", value);
|
||||
current_macro_entry = value;
|
||||
current_macro_code.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::SetGraphMacroCodeArg)) {
|
||||
// Append a new code word to the current macro.
|
||||
current_macro_code.push_back(value);
|
||||
|
||||
// There are no more params remaining, submit the code to the 3D engine.
|
||||
if (remaining_params == 0) {
|
||||
maxwell_3d->SubmitMacroCode(current_macro_entry, std::move(current_macro_code));
|
||||
current_macro_entry = InvalidGraphMacroEntry;
|
||||
current_macro_code.clear();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X", method, subchannel,
|
||||
value);
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
@@ -79,7 +54,7 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
fermi_2d->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->WriteReg(method, value, remaining_params);
|
||||
maxwell_3d->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->WriteReg(method, value);
|
||||
@@ -103,8 +78,7 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
case SubmissionMode::Increasing: {
|
||||
// Increase the method value with each argument.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr));
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
@@ -113,31 +87,27 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
case SubmissionMode::NonIncreasing: {
|
||||
// Use the same method value for all arguments.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::IncreaseOnce: {
|
||||
ASSERT(header.arg_count.Value() >= 1);
|
||||
|
||||
// Use the original method for the first argument and then the next method for all other
|
||||
// arguments.
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - 1);
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
|
||||
current_addr += sizeof(u32);
|
||||
|
||||
// Use the same method value for all arguments.
|
||||
for (unsigned i = 1; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr));
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::Inline: {
|
||||
// The register value is stored in the bits 16-28 as an immediate
|
||||
WriteReg(header.method, header.subchannel, header.inline_data, 0);
|
||||
WriteReg(header.method, header.subchannel, header.inline_data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -34,4 +34,6 @@ static_assert(std::is_standard_layout<CommandHeader>::value == true,
|
||||
"CommandHeader does not use standard layout");
|
||||
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
|
||||
|
||||
void ProcessCommandList(VAddr address, u32 size);
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -8,122 +8,17 @@
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = {
|
||||
{0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}},
|
||||
{0xE24, {"SetShader", 5, &Maxwell3D::SetShader}},
|
||||
{0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}},
|
||||
};
|
||||
|
||||
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
|
||||
void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) {
|
||||
uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
|
||||
// TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47
|
||||
|
||||
// The requested macro must have been uploaded already.
|
||||
ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded",
|
||||
method);
|
||||
|
||||
auto itr = method_handlers.find(method);
|
||||
ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method);
|
||||
|
||||
ASSERT(itr->second.arguments == parameters.size());
|
||||
|
||||
(this->*itr->second.handler)(parameters);
|
||||
|
||||
// Reset the current macro and its parameters.
|
||||
executing_macro = 0;
|
||||
macro_params.clear();
|
||||
}
|
||||
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
// It is an error to write to a register other than the current macro's ARG register before it
|
||||
// has finished execution.
|
||||
if (executing_macro != 0) {
|
||||
ASSERT(method == executing_macro + 1);
|
||||
}
|
||||
|
||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
// We're trying to execute a macro
|
||||
if (executing_macro == 0) {
|
||||
// A macro call must begin by writing the macro method's register, not its argument.
|
||||
ASSERT_MSG((method % 2) == 0,
|
||||
"Can't start macro execution by writing to the ARGS register");
|
||||
executing_macro = method;
|
||||
}
|
||||
|
||||
macro_params.push_back(value);
|
||||
|
||||
// Call the macro when there are no more parameters in the command buffer
|
||||
if (remaining_params == 0) {
|
||||
CallMacroMethod(executing_macro, macro_params);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(code_address.code_address_high):
|
||||
case MAXWELL3D_REG_INDEX(code_address.code_address_low): {
|
||||
// Note: For some reason games (like Puyo Puyo Tetris) seem to write 0 to the CODE_ADDRESS
|
||||
// register, we do not currently know if that's intended or a bug, so we assert it lest
|
||||
// stuff breaks in other places (like the shader address calculation).
|
||||
ASSERT_MSG(regs.code_address.CodeAddress() == 0, "Unexpected CODE_ADDRESS register value.");
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
|
||||
ProcessCBData(value);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Vertex);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[1].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::TesselationControl);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[2].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::TesselationEval);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[3].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Geometry);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[4].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Fragment);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): {
|
||||
DrawArrays();
|
||||
break;
|
||||
@@ -153,8 +48,7 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Query mode %u not implemented",
|
||||
static_cast<u32>(regs.query.query_get.mode.Value()));
|
||||
UNIMPLEMENTED_MSG("Query mode %u not implemented", regs.query.query_get.mode.Value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,113 +56,5 @@ void Maxwell3D::DrawArrays() {
|
||||
LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
|
||||
}
|
||||
|
||||
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Shader stage, usually 4 for FragmentShader
|
||||
*/
|
||||
|
||||
u32 stage = parameters[0];
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8;
|
||||
u32 size = regs.tex_info_buffers.size[stage];
|
||||
|
||||
regs.const_buffer.cb_size = size;
|
||||
regs.const_buffer.cb_address_high = address >> 32;
|
||||
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Shader Program.
|
||||
* [1] = Unknown, presumably the shader id.
|
||||
* [2] = Offset to the start of the shader, after the 0x30 bytes header.
|
||||
* [3] = Shader Stage.
|
||||
* [4] = Const Buffer Address >> 8.
|
||||
*/
|
||||
auto shader_program = static_cast<Regs::ShaderProgram>(parameters[0]);
|
||||
// TODO(Subv): This address is probably an offset from the CODE_ADDRESS register.
|
||||
GPUVAddr address = parameters[2];
|
||||
auto shader_stage = static_cast<Regs::ShaderStage>(parameters[3]);
|
||||
GPUVAddr cb_address = parameters[4] << 8;
|
||||
|
||||
auto& shader = state.shader_programs[static_cast<size_t>(shader_program)];
|
||||
shader.program = shader_program;
|
||||
shader.stage = shader_stage;
|
||||
shader.address = address;
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
// TODO(Subv): Early exit if register 0xD1C + shader_program contains the same as params[1].
|
||||
auto& shader_regs = regs.shader_config[static_cast<size_t>(shader_program)];
|
||||
shader_regs.start_id = address;
|
||||
// TODO(Subv): Write params[1] to register 0xD1C + shader_program.
|
||||
// TODO(Subv): Write params[2] to register 0xD22 + shader_program.
|
||||
|
||||
// Note: This value is hardcoded in the macro's code.
|
||||
static constexpr u32 DefaultCBSize = 0x10000;
|
||||
regs.const_buffer.cb_size = DefaultCBSize;
|
||||
regs.const_buffer.cb_address_high = cb_address >> 32;
|
||||
regs.const_buffer.cb_address_low = cb_address & 0xFFFFFFFF;
|
||||
|
||||
// Write a hardcoded 0x11 to CB_BIND, this binds the current const buffer to buffer c1[] in the
|
||||
// shader. It's likely that these are the constants for the shader.
|
||||
regs.cb_bind[static_cast<size_t>(shader_stage)].valid.Assign(1);
|
||||
regs.cb_bind[static_cast<size_t>(shader_stage)].index.Assign(1);
|
||||
|
||||
ProcessCBBind(shader_stage);
|
||||
}
|
||||
|
||||
void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Buffer offset >> 2
|
||||
*/
|
||||
|
||||
u32 buffer_offset = parameters[0] << 2;
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
// Note: This value is hardcoded in the macro's code.
|
||||
static constexpr u32 DefaultCBSize = 0x5F00;
|
||||
regs.const_buffer.cb_size = DefaultCBSize;
|
||||
|
||||
GPUVAddr address = regs.ssbo_info.BufferAddress();
|
||||
regs.const_buffer.cb_address_high = address >> 32;
|
||||
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
|
||||
|
||||
regs.const_buffer.cb_pos = buffer_offset;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
|
||||
auto& bind_data = regs.cb_bind[static_cast<size_t>(stage)];
|
||||
|
||||
auto& buffer = shader.const_buffers[bind_data.index];
|
||||
|
||||
buffer.enabled = bind_data.valid.Value() != 0;
|
||||
buffer.index = bind_data.index;
|
||||
buffer.address = regs.const_buffer.BufferAddress();
|
||||
buffer.size = regs.const_buffer.cb_size;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBData(u32 value) {
|
||||
// Write the input value to the current const buffer at the current position.
|
||||
GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
|
||||
ASSERT(buffer_address != 0);
|
||||
|
||||
// Don't allow writing past the end of the buffer.
|
||||
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
|
||||
|
||||
VAddr address =
|
||||
memory_manager.PhysicalToVirtualAddress(buffer_address + regs.const_buffer.cb_pos);
|
||||
|
||||
Memory::Write32(address, value);
|
||||
|
||||
// Increment the current buffer position.
|
||||
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -21,129 +18,21 @@ public:
|
||||
~Maxwell3D() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value, u32 remaining_params);
|
||||
|
||||
/// Uploads the code for a GPU macro program associated with the specified entry.
|
||||
void SubmitMacroCode(u32 entry, std::vector<u32> code);
|
||||
void WriteReg(u32 method, u32 value);
|
||||
|
||||
/// Register structure of the Maxwell3D engine.
|
||||
/// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0xE36;
|
||||
|
||||
static constexpr size_t NumRenderTargets = 8;
|
||||
static constexpr size_t NumCBData = 16;
|
||||
static constexpr size_t NumVertexArrays = 32;
|
||||
static constexpr size_t MaxShaderProgram = 6;
|
||||
static constexpr size_t MaxShaderStage = 5;
|
||||
// Maximum number of const buffers per shader stage.
|
||||
static constexpr size_t MaxConstBuffers = 16;
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
Write = 0,
|
||||
Sync = 1,
|
||||
};
|
||||
|
||||
enum class ShaderProgram : u32 {
|
||||
VertexA = 0,
|
||||
VertexB = 1,
|
||||
TesselationControl = 2,
|
||||
TesselationEval = 3,
|
||||
Geometry = 4,
|
||||
Fragment = 5,
|
||||
};
|
||||
|
||||
enum class ShaderStage : u32 {
|
||||
Vertex = 0,
|
||||
TesselationControl = 1,
|
||||
TesselationEval = 2,
|
||||
Geometry = 3,
|
||||
Fragment = 4,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x200);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
u32 horiz;
|
||||
u32 vert;
|
||||
u32 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];
|
||||
|
||||
INSERT_PADDING_WORDS(0x178);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
u32 format;
|
||||
u32 block_dimensions;
|
||||
u32 layer_stride;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} zeta;
|
||||
|
||||
INSERT_PADDING_WORDS(0x8A);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 4, u32> count;
|
||||
};
|
||||
} rt_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0xCF);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
u32 tsc_address_low;
|
||||
u32 tsc_limit;
|
||||
|
||||
GPUVAddr TSCAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(tsc_address_high) << 32) | tsc_address_low);
|
||||
}
|
||||
} tsc;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3);
|
||||
|
||||
struct {
|
||||
u32 tic_address_high;
|
||||
u32 tic_address_low;
|
||||
u32 tic_limit;
|
||||
|
||||
GPUVAddr TICAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(tic_address_high) << 32) | tic_address_low);
|
||||
}
|
||||
} tic;
|
||||
|
||||
INSERT_PADDING_WORDS(0x22);
|
||||
|
||||
struct {
|
||||
u32 code_address_high;
|
||||
u32 code_address_low;
|
||||
|
||||
GPUVAddr CodeAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low);
|
||||
}
|
||||
} code_address;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
INSERT_PADDING_WORDS(0x585);
|
||||
struct {
|
||||
u32 vertex_end_gl;
|
||||
u32 vertex_begin_gl;
|
||||
@@ -165,98 +54,7 @@ public:
|
||||
(static_cast<GPUVAddr>(query_address_high) << 32) | query_address_low);
|
||||
}
|
||||
} query;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3C);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 12, u32> stride;
|
||||
BitField<12, 1, u32> enable;
|
||||
};
|
||||
u32 start_high;
|
||||
u32 start_low;
|
||||
u32 divisor;
|
||||
|
||||
GPUVAddr StartAddress() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_high) << 32) |
|
||||
start_low);
|
||||
}
|
||||
} vertex_array[NumVertexArrays];
|
||||
|
||||
INSERT_PADDING_WORDS(0x40);
|
||||
|
||||
struct {
|
||||
u32 limit_high;
|
||||
u32 limit_low;
|
||||
|
||||
GPUVAddr LimitAddress() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) |
|
||||
limit_low);
|
||||
}
|
||||
} vertex_array_limit[NumVertexArrays];
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<4, 4, ShaderProgram> program;
|
||||
};
|
||||
u32 start_id;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 gpr_alloc;
|
||||
ShaderStage type;
|
||||
INSERT_PADDING_WORDS(9);
|
||||
} shader_config[MaxShaderProgram];
|
||||
|
||||
INSERT_PADDING_WORDS(0x8C);
|
||||
|
||||
struct {
|
||||
u32 cb_size;
|
||||
u32 cb_address_high;
|
||||
u32 cb_address_low;
|
||||
u32 cb_pos;
|
||||
u32 cb_data[NumCBData];
|
||||
|
||||
GPUVAddr BufferAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(cb_address_high) << 32) | cb_address_low);
|
||||
}
|
||||
} const_buffer;
|
||||
|
||||
INSERT_PADDING_WORDS(0x10);
|
||||
|
||||
struct {
|
||||
union {
|
||||
u32 raw_config;
|
||||
BitField<0, 1, u32> valid;
|
||||
BitField<4, 5, u32> index;
|
||||
};
|
||||
INSERT_PADDING_WORDS(7);
|
||||
} cb_bind[MaxShaderStage];
|
||||
|
||||
INSERT_PADDING_WORDS(0x56);
|
||||
|
||||
u32 tex_cb_index;
|
||||
|
||||
INSERT_PADDING_WORDS(0x395);
|
||||
|
||||
struct {
|
||||
/// Compressed address of a buffer that holds information about bound SSBOs.
|
||||
/// This address is usually bound to c0 in the shaders.
|
||||
u32 buffer_address;
|
||||
|
||||
GPUVAddr BufferAddress() const {
|
||||
return static_cast<GPUVAddr>(buffer_address) << 8;
|
||||
}
|
||||
} ssbo_info;
|
||||
|
||||
INSERT_PADDING_WORDS(0x11);
|
||||
|
||||
struct {
|
||||
u32 address[MaxShaderStage];
|
||||
u32 size[MaxShaderStage];
|
||||
} tex_info_buffers;
|
||||
|
||||
INSERT_PADDING_WORDS(0x102);
|
||||
INSERT_PADDING_WORDS(0x772);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
@@ -264,94 +62,21 @@ public:
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
|
||||
|
||||
struct State {
|
||||
struct ConstBufferInfo {
|
||||
GPUVAddr address;
|
||||
u32 index;
|
||||
u32 size;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct ShaderProgramInfo {
|
||||
Regs::ShaderStage stage;
|
||||
Regs::ShaderProgram program;
|
||||
GPUVAddr address;
|
||||
};
|
||||
|
||||
struct ShaderStageInfo {
|
||||
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
|
||||
};
|
||||
|
||||
std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
|
||||
std::array<ShaderProgramInfo, Regs::MaxShaderProgram> shader_programs;
|
||||
};
|
||||
|
||||
State state{};
|
||||
|
||||
private:
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
|
||||
|
||||
/// Macro method that is currently being executed / being fed parameters.
|
||||
u32 executing_macro = 0;
|
||||
/// Parameters that have been submitted to the macro call so far.
|
||||
std::vector<u32> macro_params;
|
||||
|
||||
/**
|
||||
* Call a macro on this engine.
|
||||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
|
||||
|
||||
/// Handles a write to the QUERY_GET register.
|
||||
void ProcessQueryGet();
|
||||
|
||||
/// Handles a write to the CB_DATA[i] register.
|
||||
void ProcessCBData(u32 value);
|
||||
|
||||
/// Handles a write to the CB_BIND register.
|
||||
void ProcessCBBind(Regs::ShaderStage stage);
|
||||
|
||||
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
|
||||
void DrawArrays();
|
||||
|
||||
/// Method call handlers
|
||||
void BindTextureInfoBuffer(const std::vector<u32>& parameters);
|
||||
void SetShader(const std::vector<u32>& parameters);
|
||||
void BindStorageBuffer(const std::vector<u32>& parameters);
|
||||
|
||||
struct MethodInfo {
|
||||
const char* name;
|
||||
u32 arguments;
|
||||
void (Maxwell3D::*handler)(const std::vector<u32>& parameters);
|
||||
};
|
||||
|
||||
static const std::unordered_map<u32, MethodInfo> method_handlers;
|
||||
MemoryManager& memory_manager;
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(rt, 0x200);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
ASSERT_REG_POSITION(cb_bind[0], 0x904);
|
||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
|
||||
ASSERT_REG_POSITION(ssbo_info, 0xD18);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
GPU::GPU() {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -6,18 +6,14 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
FERMI_TWOD_A = 0x902D, // 2D Engine
|
||||
MAXWELL_B = 0xB197, // 3D Engine
|
||||
@@ -28,8 +24,13 @@ enum class EngineID {
|
||||
|
||||
class GPU final {
|
||||
public:
|
||||
GPU();
|
||||
~GPU();
|
||||
GPU() {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
}
|
||||
~GPU() = default;
|
||||
|
||||
/// Processes a command list stored at the specified address in GPU memory.
|
||||
void ProcessCommandList(GPUVAddr address, u32 size);
|
||||
@@ -37,10 +38,8 @@ public:
|
||||
std::unique_ptr<MemoryManager> memory_manager;
|
||||
|
||||
private:
|
||||
static constexpr u32 InvalidGraphMacroEntry = 0xFFFFFFFF;
|
||||
|
||||
/// Writes a single register in the engine bound to the specified subchannel
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value);
|
||||
|
||||
/// Mapping of command subchannels to their bound engine ids.
|
||||
std::unordered_map<u32, EngineID> bound_engines;
|
||||
@@ -51,11 +50,6 @@ private:
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
|
||||
/// Entry of the macro that is currently being uploaded
|
||||
u32 current_macro_entry = InvalidGraphMacroEntry;
|
||||
/// Code being uploaded for the current macro
|
||||
std::vector<u32> current_macro_code;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
struct ScreenInfo;
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
class RasterizerInterface {
|
||||
public:
|
||||
virtual ~RasterizerInterface() {}
|
||||
|
||||
/// Draw the current batch of triangles
|
||||
virtual void DrawTriangles() = 0;
|
||||
|
||||
/// Notify rasterizer that the specified Maxwell register has been changed
|
||||
virtual void NotifyMaxwellRegisterChanged(u32 id) = 0;
|
||||
|
||||
/// Notify rasterizer that all caches should be flushed to 3DS memory
|
||||
virtual void FlushAll() = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
|
||||
virtual void FlushRegion(PAddr addr, u32 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be invalidated
|
||||
virtual void InvalidateRegion(PAddr addr, u32 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
|
||||
/// and invalidated
|
||||
virtual void FlushAndInvalidateRegion(PAddr addr, u32 size) = 0;
|
||||
|
||||
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0
|
||||
virtual bool AccelerateDisplayTransfer(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 1
|
||||
virtual bool AccelerateTextureCopy(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to fill a region
|
||||
virtual bool AccelerateFill(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to display the framebuffer to screen
|
||||
virtual bool AccelerateDisplay(const void* config, PAddr framebuffer_addr, u32 pixel_stride,
|
||||
ScreenInfo& screen_info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool AccelerateDrawBatch(bool is_indexed) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace VideoCore
|
||||
@@ -1,269 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <glad/glad.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
|
||||
using PixelFormat = SurfaceParams::PixelFormat;
|
||||
using SurfaceType = SurfaceParams::SurfaceType;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||
|
||||
enum class UniformBindings : GLuint { Common, VS, FS };
|
||||
|
||||
static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding,
|
||||
size_t expected_size) {
|
||||
GLuint ub_index = glGetUniformBlockIndex(shader, name);
|
||||
if (ub_index != GL_INVALID_INDEX) {
|
||||
GLint ub_size = 0;
|
||||
glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
|
||||
ASSERT_MSG(ub_size == expected_size,
|
||||
"Uniform block size did not match! Got %d, expected %zu",
|
||||
static_cast<int>(ub_size), expected_size);
|
||||
glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
|
||||
}
|
||||
}
|
||||
|
||||
static void SetShaderUniformBlockBindings(GLuint shader) {
|
||||
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
|
||||
sizeof(RasterizerOpenGL::UniformData));
|
||||
SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS,
|
||||
sizeof(RasterizerOpenGL::VSUniformData));
|
||||
SetShaderUniformBlockBinding(shader, "fs_config", UniformBindings::FS,
|
||||
sizeof(RasterizerOpenGL::FSUniformData));
|
||||
}
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL() {
|
||||
has_ARB_buffer_storage = false;
|
||||
has_ARB_direct_state_access = false;
|
||||
has_ARB_separate_shader_objects = false;
|
||||
has_ARB_vertex_attrib_binding = false;
|
||||
|
||||
GLint ext_num;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
|
||||
for (GLint i = 0; i < ext_num; i++) {
|
||||
std::string extension{reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))};
|
||||
|
||||
if (extension == "GL_ARB_buffer_storage") {
|
||||
has_ARB_buffer_storage = true;
|
||||
} else if (extension == "GL_ARB_direct_state_access") {
|
||||
has_ARB_direct_state_access = true;
|
||||
} else if (extension == "GL_ARB_separate_shader_objects") {
|
||||
has_ARB_separate_shader_objects = true;
|
||||
} else if (extension == "GL_ARB_vertex_attrib_binding") {
|
||||
has_ARB_vertex_attrib_binding = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
||||
state.clip_distance[0] = true;
|
||||
|
||||
// Generate VBO, VAO and UBO
|
||||
vertex_buffer = OGLStreamBuffer::MakeBuffer(GLAD_GL_ARB_buffer_storage, GL_ARRAY_BUFFER);
|
||||
vertex_buffer->Create(VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE / 2);
|
||||
sw_vao.Create();
|
||||
uniform_buffer.Create();
|
||||
|
||||
state.draw.vertex_array = sw_vao.handle;
|
||||
state.draw.vertex_buffer = vertex_buffer->GetHandle();
|
||||
state.draw.uniform_buffer = uniform_buffer.handle;
|
||||
state.Apply();
|
||||
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), nullptr, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle);
|
||||
|
||||
uniform_block_data.dirty = true;
|
||||
|
||||
// Create render framebuffer
|
||||
framebuffer.Create();
|
||||
|
||||
if (has_ARB_separate_shader_objects) {
|
||||
hw_vao.Create();
|
||||
hw_vao_enabled_attributes.fill(false);
|
||||
|
||||
stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER);
|
||||
stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2);
|
||||
state.draw.vertex_buffer = stream_buffer->GetHandle();
|
||||
|
||||
pipeline.Create();
|
||||
vs_input_index_min = 0;
|
||||
vs_input_index_max = 0;
|
||||
state.draw.program_pipeline = pipeline.handle;
|
||||
state.draw.shader_program = 0;
|
||||
state.draw.vertex_array = hw_vao.handle;
|
||||
state.Apply();
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle());
|
||||
|
||||
vs_uniform_buffer.Create();
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, vs_uniform_buffer.handle);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(VSUniformData), nullptr, GL_STREAM_COPY);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, vs_uniform_buffer.handle);
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
accelerate_draw = AccelDraw::Disabled;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
// Sync fixed function OpenGL state
|
||||
SyncClipEnabled();
|
||||
SyncClipCoef();
|
||||
SyncCullMode();
|
||||
SyncBlendEnabled();
|
||||
SyncBlendFuncs();
|
||||
SyncBlendColor();
|
||||
}
|
||||
|
||||
RasterizerOpenGL::~RasterizerOpenGL() {
|
||||
if (stream_buffer != nullptr) {
|
||||
state.draw.vertex_buffer = stream_buffer->GetHandle();
|
||||
state.Apply();
|
||||
stream_buffer->Release();
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::array<GLenum, 4> vs_attrib_types{
|
||||
GL_BYTE, // VertexAttributeFormat::BYTE
|
||||
GL_UNSIGNED_BYTE, // VertexAttributeFormat::UBYTE
|
||||
GL_SHORT, // VertexAttributeFormat::SHORT
|
||||
GL_FLOAT // VertexAttributeFormat::FLOAT
|
||||
};
|
||||
|
||||
void RasterizerOpenGL::AnalyzeVertexArray(bool is_indexed) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) {
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset) {
|
||||
MICROPROFILE_SCOPE(OpenGL_VS);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset) {
|
||||
MICROPROFILE_SCOPE(OpenGL_FS);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
|
||||
if (!has_ARB_separate_shader_objects) {
|
||||
UNIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
|
||||
DrawTriangles();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::DrawTriangles() {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {}
|
||||
|
||||
void RasterizerOpenGL::FlushAll() {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushAll();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.InvalidateRegion(addr, size, nullptr);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.FlushRegion(addr, size);
|
||||
res_cache.InvalidateRegion(addr, size, nullptr);
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
UNIMPLEMENTED();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateTextureCopy(const void* config) {
|
||||
UNIMPLEMENTED();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateFill(const void* config) {
|
||||
UNIMPLEMENTED();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateDisplay(const void* config, PAddr framebuffer_addr,
|
||||
u32 pixel_stride, ScreenInfo& screen_info) {
|
||||
UNIMPLEMENTED();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetShader() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipCoef() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncCullMode() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncDepthScale() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncDepthOffset() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendEnabled() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendFuncs() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendColor() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
struct ScreenInfo;
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
|
||||
public:
|
||||
RasterizerOpenGL();
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void DrawTriangles() override;
|
||||
void NotifyMaxwellRegisterChanged(u32 id) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(PAddr addr, u32 size) override;
|
||||
void InvalidateRegion(PAddr addr, u32 size) override;
|
||||
void FlushAndInvalidateRegion(PAddr addr, u32 size) override;
|
||||
bool AccelerateDisplayTransfer(const void* config) override;
|
||||
bool AccelerateTextureCopy(const void* config) override;
|
||||
bool AccelerateFill(const void* config) override;
|
||||
bool AccelerateDisplay(const void* config, PAddr framebuffer_addr, u32 pixel_stride,
|
||||
ScreenInfo& screen_info) override;
|
||||
bool AccelerateDrawBatch(bool is_indexed) override;
|
||||
|
||||
struct VertexShader {
|
||||
OGLShader shader;
|
||||
};
|
||||
|
||||
struct FragmentShader {
|
||||
OGLShader shader;
|
||||
};
|
||||
|
||||
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
|
||||
// NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at
|
||||
// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||
// Not following that rule will cause problems on some AMD drivers.
|
||||
struct UniformData {};
|
||||
|
||||
// static_assert(
|
||||
// sizeof(UniformData) == 0x460,
|
||||
// "The size of the UniformData structure has changed, update the structure in the shader");
|
||||
static_assert(sizeof(UniformData) < 16384,
|
||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
struct VSUniformData {};
|
||||
// static_assert(
|
||||
// sizeof(VSUniformData) == 1856,
|
||||
// "The size of the VSUniformData structure has changed, update the structure in the
|
||||
// shader");
|
||||
static_assert(sizeof(VSUniformData) < 16384,
|
||||
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
struct FSUniformData {};
|
||||
// static_assert(
|
||||
// sizeof(FSUniformData) == 1856,
|
||||
// "The size of the FSUniformData structure has changed, update the structure in the
|
||||
// shader");
|
||||
static_assert(sizeof(FSUniformData) < 16384,
|
||||
"FSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
private:
|
||||
struct SamplerInfo {};
|
||||
|
||||
/// Syncs the clip enabled status to match the guest state
|
||||
void SyncClipEnabled();
|
||||
|
||||
/// Syncs the clip coefficients to match the guest state
|
||||
void SyncClipCoef();
|
||||
|
||||
/// Sets the OpenGL shader in accordance with the current guest state
|
||||
void SetShader();
|
||||
|
||||
/// Syncs the cull mode to match the guest state
|
||||
void SyncCullMode();
|
||||
|
||||
/// Syncs the depth scale to match the guest state
|
||||
void SyncDepthScale();
|
||||
|
||||
/// Syncs the depth offset to match the guest state
|
||||
void SyncDepthOffset();
|
||||
|
||||
/// Syncs the blend enabled status to match the guest state
|
||||
void SyncBlendEnabled();
|
||||
|
||||
/// Syncs the blend functions to match the guest state
|
||||
void SyncBlendFuncs();
|
||||
|
||||
/// Syncs the blend color to match the guest state
|
||||
void SyncBlendColor();
|
||||
|
||||
bool has_ARB_buffer_storage;
|
||||
bool has_ARB_direct_state_access;
|
||||
bool has_ARB_separate_shader_objects;
|
||||
bool has_ARB_vertex_attrib_binding;
|
||||
|
||||
OpenGLState state;
|
||||
|
||||
RasterizerCacheOpenGL res_cache;
|
||||
|
||||
struct {
|
||||
UniformData data;
|
||||
bool dirty;
|
||||
} uniform_block_data = {};
|
||||
|
||||
OGLPipeline pipeline;
|
||||
OGLVertexArray sw_vao;
|
||||
OGLVertexArray hw_vao;
|
||||
std::array<bool, 16> hw_vao_enabled_attributes;
|
||||
|
||||
std::array<SamplerInfo, 3> texture_samplers;
|
||||
static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> vertex_buffer;
|
||||
OGLBuffer uniform_buffer;
|
||||
OGLFramebuffer framebuffer;
|
||||
|
||||
static constexpr size_t STREAM_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> stream_buffer;
|
||||
|
||||
GLint vs_input_index_min;
|
||||
GLint vs_input_index_max;
|
||||
GLsizeiptr vs_input_size;
|
||||
|
||||
void AnalyzeVertexArray(bool is_indexed);
|
||||
void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset);
|
||||
|
||||
OGLBuffer vs_uniform_buffer;
|
||||
std::unordered_map<GLShader::MaxwellVSConfig, VertexShader*> vs_shader_map;
|
||||
std::unordered_map<std::string, VertexShader> vs_shader_cache;
|
||||
OGLShader vs_default_shader;
|
||||
|
||||
void SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset);
|
||||
|
||||
OGLBuffer fs_uniform_buffer;
|
||||
std::unordered_map<GLShader::MaxwellFSConfig, FragmentShader*> fs_shader_map;
|
||||
std::unordered_map<std::string, FragmentShader> fs_shader_cache;
|
||||
OGLShader fs_default_shader;
|
||||
|
||||
void SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset);
|
||||
|
||||
enum class AccelDraw { Disabled, Arrays, Indexed };
|
||||
AccelDraw accelerate_draw;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,350 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#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 <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
struct CachedSurface;
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
using SurfaceSet = std::set<Surface>;
|
||||
|
||||
using SurfaceRegions = boost::icl::interval_set<PAddr>;
|
||||
using SurfaceMap = boost::icl::interval_map<PAddr, Surface>;
|
||||
using SurfaceCache = boost::icl::interval_map<PAddr, 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<u32, 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 {
|
||||
// First 5 formats are shared between textures and color buffers
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
|
||||
// Texture-only formats
|
||||
IA8 = 5,
|
||||
RG8 = 6,
|
||||
I8 = 7,
|
||||
A8 = 8,
|
||||
IA4 = 9,
|
||||
I4 = 10,
|
||||
A4 = 11,
|
||||
ETC1 = 12,
|
||||
ETC1A4 = 13,
|
||||
|
||||
// Depth buffer-only formats
|
||||
D16 = 14,
|
||||
// gap
|
||||
D24 = 16,
|
||||
D24S8 = 17,
|
||||
|
||||
Invalid = 255,
|
||||
};
|
||||
|
||||
enum class SurfaceType {
|
||||
Color = 0,
|
||||
Texture = 1,
|
||||
Depth = 2,
|
||||
DepthStencil = 3,
|
||||
Fill = 4,
|
||||
Invalid = 5
|
||||
};
|
||||
|
||||
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
|
||||
constexpr std::array<unsigned int, 18> bpp_table = {
|
||||
32, // RGBA8
|
||||
24, // RGB8
|
||||
16, // RGB5A1
|
||||
16, // RGB565
|
||||
16, // RGBA4
|
||||
16, // IA8
|
||||
16, // RG8
|
||||
8, // I8
|
||||
8, // A8
|
||||
8, // IA4
|
||||
4, // I4
|
||||
4, // A4
|
||||
4, // ETC1
|
||||
8, // ETC1A4
|
||||
16, // D16
|
||||
0,
|
||||
24, // D24
|
||||
32, // D24S8
|
||||
};
|
||||
|
||||
assert(static_cast<size_t>(format) < bpp_table.size());
|
||||
return bpp_table[static_cast<size_t>(format)];
|
||||
}
|
||||
unsigned int GetFormatBpp() const {
|
||||
return GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
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::Color || a_type == SurfaceType::Texture) &&
|
||||
(b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||
if ((unsigned int)pixel_format < 5) {
|
||||
return SurfaceType::Color;
|
||||
}
|
||||
|
||||
if ((unsigned int)pixel_format < 14) {
|
||||
return SurfaceType::Texture;
|
||||
}
|
||||
|
||||
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
|
||||
return SurfaceType::Depth;
|
||||
}
|
||||
|
||||
if (pixel_format == PixelFormat::D24S8) {
|
||||
return SurfaceType::DepthStencil;
|
||||
}
|
||||
|
||||
return SurfaceType::Invalid;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
|
||||
MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
|
||||
|
||||
PAddr addr = 0;
|
||||
PAddr end = 0;
|
||||
u64 size = 0;
|
||||
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 stride = 0;
|
||||
u16 res_scale = 1;
|
||||
|
||||
bool is_tiled = false;
|
||||
PixelFormat pixel_format = PixelFormat::Invalid;
|
||||
SurfaceType type = SurfaceType::Invalid;
|
||||
};
|
||||
|
||||
struct CachedSurface : SurfaceParams {
|
||||
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
|
||||
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
|
||||
|
||||
bool IsRegionValid(SurfaceInterval interval) const {
|
||||
return (invalid_regions.find(interval) == invalid_regions.end());
|
||||
}
|
||||
|
||||
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) {
|
||||
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
|
||||
return format == PixelFormat::Invalid
|
||||
? 0
|
||||
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
|
||||
? 4
|
||||
: SurfaceParams::GetFormatBpp(format) / 8;
|
||||
}
|
||||
|
||||
std::unique_ptr<u8[]> gl_buffer;
|
||||
size_t gl_buffer_size = 0;
|
||||
|
||||
// Read/Write data in 3DS memory to/from gl_buffer
|
||||
void LoadGLBuffer(PAddr load_start, PAddr load_end);
|
||||
void FlushGLBuffer(PAddr flush_start, PAddr flush_end);
|
||||
|
||||
// Upload/Download data in gl_buffer in/to this surface's texture
|
||||
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 : 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 3DS memory to OpenGL and cache it (if not already cached)
|
||||
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||
bool load_if_create);
|
||||
|
||||
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
|
||||
/// 3DS 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 void* config);
|
||||
|
||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||
const MathUtil::Rectangle<s32>& viewport_rect);
|
||||
|
||||
/// Get a surface that matches the fill config
|
||||
Surface GetFillSurface(const void* config);
|
||||
|
||||
/// 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(PAddr addr, u64 size, Surface flush_surface = nullptr);
|
||||
|
||||
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
|
||||
void InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner);
|
||||
|
||||
/// Flush all cached resources tracked by this cache manager
|
||||
void FlushAll();
|
||||
|
||||
private:
|
||||
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
|
||||
|
||||
/// Update surface's texture for given region when necessary
|
||||
void ValidateSurface(const Surface& surface, PAddr addr, u64 size);
|
||||
|
||||
/// Create a new surface
|
||||
Surface CreateSurface(const SurfaceParams& params);
|
||||
|
||||
/// Register surface into the cache
|
||||
void RegisterSurface(const Surface& surface);
|
||||
|
||||
/// Remove surface from the cache
|
||||
void UnregisterSurface(const Surface& surface);
|
||||
|
||||
/// Increase/decrease the number of surface in pages touching the specified region
|
||||
void UpdatePagesCachedCount(PAddr addr, u64 size, int delta);
|
||||
|
||||
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;
|
||||
OGLShader d24s8_abgr_shader;
|
||||
GLint d24s8_abgr_tbo_size_u_id;
|
||||
GLint d24s8_abgr_viewport_u_id;
|
||||
};
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteTextures(1, &handle);
|
||||
OpenGLState::GetCurState().ResetTexture(handle).Apply();
|
||||
OpenGLState::ResetTexture(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteSamplers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetSampler(handle).Apply();
|
||||
OpenGLState::ResetSampler(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@@ -91,13 +91,10 @@ public:
|
||||
}
|
||||
|
||||
/// Creates a new internal OpenGL resource and stores the handle
|
||||
void Create(const char* vert_shader, const char* geo_shader, const char* frag_shader,
|
||||
const std::vector<const char*>& feedback_vars = {},
|
||||
bool separable_program = false) {
|
||||
void Create(const char* vert_shader, const char* frag_shader) {
|
||||
if (handle != 0)
|
||||
return;
|
||||
handle = GLShader::LoadProgram(vert_shader, geo_shader, frag_shader, feedback_vars,
|
||||
separable_program);
|
||||
handle = GLShader::LoadProgram(vert_shader, frag_shader);
|
||||
}
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
@@ -105,40 +102,7 @@ public:
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteProgram(handle);
|
||||
OpenGLState::GetCurState().ResetProgram(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
class OGLPipeline : private NonCopyable {
|
||||
public:
|
||||
OGLPipeline() = default;
|
||||
OGLPipeline(OGLPipeline&& o) {
|
||||
handle = std::exchange<GLuint>(o.handle, 0);
|
||||
}
|
||||
~OGLPipeline() {
|
||||
Release();
|
||||
}
|
||||
OGLPipeline& operator=(OGLPipeline&& o) {
|
||||
handle = std::exchange<GLuint>(o.handle, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Creates a new internal OpenGL resource and stores the handle
|
||||
void Create() {
|
||||
if (handle != 0)
|
||||
return;
|
||||
glGenProgramPipelines(1, &handle);
|
||||
}
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release() {
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteProgramPipelines(1, &handle);
|
||||
OpenGLState::GetCurState().ResetPipeline(handle).Apply();
|
||||
OpenGLState::ResetProgram(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@@ -171,46 +135,13 @@ public:
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteBuffers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
|
||||
OpenGLState::ResetBuffer(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
class OGLSync : private NonCopyable {
|
||||
public:
|
||||
OGLSync() = default;
|
||||
|
||||
OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {}
|
||||
|
||||
~OGLSync() {
|
||||
Release();
|
||||
}
|
||||
OGLSync& operator=(OGLSync&& o) {
|
||||
Release();
|
||||
handle = std::exchange(o.handle, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Creates a new internal OpenGL resource and stores the handle
|
||||
void Create() {
|
||||
if (handle != 0)
|
||||
return;
|
||||
handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release() {
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteSync(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
GLsync handle = 0;
|
||||
};
|
||||
|
||||
class OGLVertexArray : private NonCopyable {
|
||||
public:
|
||||
OGLVertexArray() = default;
|
||||
@@ -237,7 +168,7 @@ public:
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteVertexArrays(1, &handle);
|
||||
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
|
||||
OpenGLState::ResetVertexArray(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@@ -270,7 +201,7 @@ public:
|
||||
if (handle == 0)
|
||||
return;
|
||||
glDeleteFramebuffers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
|
||||
OpenGLState::ResetFramebuffer(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
|
||||
namespace Maxwell3D {
|
||||
namespace Shader {
|
||||
namespace Decompiler {
|
||||
|
||||
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
|
||||
|
||||
class Impl {
|
||||
public:
|
||||
Impl(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code,
|
||||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, u32 main_offset,
|
||||
const std::function<std::string(u32)>& inputreg_getter,
|
||||
const std::function<std::string(u32)>& outputreg_getter, bool sanitize_mul,
|
||||
const std::string& emit_cb, const std::string& setemit_cb)
|
||||
: program_code(program_code), swizzle_data(swizzle_data), main_offset(main_offset),
|
||||
inputreg_getter(inputreg_getter), outputreg_getter(outputreg_getter),
|
||||
sanitize_mul(sanitize_mul), emit_cb(emit_cb), setemit_cb(setemit_cb) {}
|
||||
|
||||
std::string Decompile() {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code;
|
||||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data;
|
||||
u32 main_offset;
|
||||
const std::function<std::string(u32)>& inputreg_getter;
|
||||
const std::function<std::string(u32)>& outputreg_getter;
|
||||
bool sanitize_mul;
|
||||
const std::string& emit_cb;
|
||||
const std::string& setemit_cb;
|
||||
};
|
||||
|
||||
std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code,
|
||||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data,
|
||||
u32 main_offset,
|
||||
const std::function<std::string(u32)>& inputreg_getter,
|
||||
const std::function<std::string(u32)>& outputreg_getter,
|
||||
bool sanitize_mul, const std::string& emit_cb,
|
||||
const std::string& setemit_cb) {
|
||||
Impl impl(program_code, swizzle_data, main_offset, inputreg_getter, outputreg_getter,
|
||||
sanitize_mul, emit_cb, setemit_cb);
|
||||
return impl.Decompile();
|
||||
}
|
||||
|
||||
} // namespace Decompiler
|
||||
} // namespace Shader
|
||||
} // namespace Maxwell3D
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Maxwell3D {
|
||||
namespace Shader {
|
||||
namespace Decompiler {
|
||||
|
||||
constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100000};
|
||||
constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100000};
|
||||
|
||||
std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code,
|
||||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data,
|
||||
u32 main_offset,
|
||||
const std::function<std::string(u32)>& inputreg_getter,
|
||||
const std::function<std::string(u32)>& outputreg_getter,
|
||||
bool sanitize_mul, const std::string& emit_cb = "",
|
||||
const std::string& setemit_cb = "");
|
||||
|
||||
} // namespace Decompiler
|
||||
} // namespace Shader
|
||||
} // namespace Maxwell3D
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
std::string GenerateVertexShader(const MaxwellVSConfig& config) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string GenerateFragmentShader(const MaxwellFSConfig& config) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace GLShader
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include "common/hash.h"
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
enum Attributes {
|
||||
ATTRIBUTE_POSITION,
|
||||
ATTRIBUTE_COLOR,
|
||||
ATTRIBUTE_TEXCOORD0,
|
||||
ATTRIBUTE_TEXCOORD1,
|
||||
ATTRIBUTE_TEXCOORD2,
|
||||
ATTRIBUTE_TEXCOORD0_W,
|
||||
ATTRIBUTE_NORMQUAT,
|
||||
ATTRIBUTE_VIEW,
|
||||
};
|
||||
|
||||
struct MaxwellShaderConfigCommon {
|
||||
explicit MaxwellShaderConfigCommon(){};
|
||||
};
|
||||
|
||||
struct MaxwellVSConfig : MaxwellShaderConfigCommon {
|
||||
explicit MaxwellVSConfig() : MaxwellShaderConfigCommon() {}
|
||||
|
||||
bool operator==(const MaxwellVSConfig& o) const {
|
||||
return std::memcmp(this, &o, sizeof(MaxwellVSConfig)) == 0;
|
||||
};
|
||||
};
|
||||
|
||||
struct MaxwellFSConfig : MaxwellShaderConfigCommon {
|
||||
explicit MaxwellFSConfig() : MaxwellShaderConfigCommon() {}
|
||||
|
||||
bool operator==(const MaxwellFSConfig& o) const {
|
||||
return std::memcmp(this, &o, sizeof(MaxwellFSConfig)) == 0;
|
||||
};
|
||||
};
|
||||
|
||||
std::string GenerateVertexShader(const MaxwellVSConfig& config);
|
||||
std::string GenerateFragmentShader(const MaxwellFSConfig& config);
|
||||
|
||||
} // namespace GLShader
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<GLShader::MaxwellVSConfig> {
|
||||
size_t operator()(const GLShader::MaxwellVSConfig& k) const {
|
||||
return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellVSConfig));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<GLShader::MaxwellFSConfig> {
|
||||
size_t operator()(const GLShader::MaxwellFSConfig& k) const {
|
||||
return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellFSConfig));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
@@ -10,85 +10,53 @@
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
|
||||
const char* fragment_shader, const std::vector<const char*>& feedback_vars,
|
||||
bool separable_program) {
|
||||
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
||||
|
||||
// Create the shaders
|
||||
GLuint vertex_shader_id = vertex_shader ? glCreateShader(GL_VERTEX_SHADER) : 0;
|
||||
GLuint geometry_shader_id = geometry_shader ? glCreateShader(GL_GEOMETRY_SHADER) : 0;
|
||||
GLuint fragment_shader_id = fragment_shader ? glCreateShader(GL_FRAGMENT_SHADER) : 0;
|
||||
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
GLint result = GL_FALSE;
|
||||
int info_log_length;
|
||||
|
||||
if (vertex_shader) {
|
||||
// Compile Vertex Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
|
||||
// Compile Vertex Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
|
||||
|
||||
glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
|
||||
glCompileShader(vertex_shader_id);
|
||||
glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
|
||||
glCompileShader(vertex_shader_id);
|
||||
|
||||
// Check Vertex Shader
|
||||
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
|
||||
glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
// Check Vertex Shader
|
||||
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
|
||||
glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
|
||||
if (info_log_length > 1) {
|
||||
std::vector<char> vertex_shader_error(info_log_length);
|
||||
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
|
||||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s",
|
||||
&vertex_shader_error[0]);
|
||||
}
|
||||
if (info_log_length > 1) {
|
||||
std::vector<char> vertex_shader_error(info_log_length);
|
||||
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
|
||||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (geometry_shader) {
|
||||
// Compile Geometry Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling geometry shader...");
|
||||
// Compile Fragment Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
|
||||
|
||||
glShaderSource(geometry_shader_id, 1, &geometry_shader, nullptr);
|
||||
glCompileShader(geometry_shader_id);
|
||||
glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
|
||||
glCompileShader(fragment_shader_id);
|
||||
|
||||
// Check Geometry Shader
|
||||
glGetShaderiv(geometry_shader_id, GL_COMPILE_STATUS, &result);
|
||||
glGetShaderiv(geometry_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
// Check Fragment Shader
|
||||
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
|
||||
glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
|
||||
if (info_log_length > 1) {
|
||||
std::vector<char> geometry_shader_error(info_log_length);
|
||||
glGetShaderInfoLog(geometry_shader_id, info_log_length, nullptr,
|
||||
&geometry_shader_error[0]);
|
||||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &geometry_shader_error[0]);
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Error compiling geometry shader:\n%s",
|
||||
&geometry_shader_error[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fragment_shader) {
|
||||
// Compile Fragment Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
|
||||
|
||||
glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
|
||||
glCompileShader(fragment_shader_id);
|
||||
|
||||
// Check Fragment Shader
|
||||
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
|
||||
glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
|
||||
if (info_log_length > 1) {
|
||||
std::vector<char> fragment_shader_error(info_log_length);
|
||||
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr,
|
||||
&fragment_shader_error[0]);
|
||||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
|
||||
&fragment_shader_error[0]);
|
||||
}
|
||||
if (info_log_length > 1) {
|
||||
std::vector<char> fragment_shader_error(info_log_length);
|
||||
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
|
||||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
|
||||
} else {
|
||||
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
|
||||
&fragment_shader_error[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,25 +64,8 @@ GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
|
||||
LOG_DEBUG(Render_OpenGL, "Linking program...");
|
||||
|
||||
GLuint program_id = glCreateProgram();
|
||||
if (vertex_shader) {
|
||||
glAttachShader(program_id, vertex_shader_id);
|
||||
}
|
||||
if (geometry_shader) {
|
||||
glAttachShader(program_id, geometry_shader_id);
|
||||
}
|
||||
if (fragment_shader) {
|
||||
glAttachShader(program_id, fragment_shader_id);
|
||||
}
|
||||
|
||||
if (!feedback_vars.empty()) {
|
||||
auto varyings = feedback_vars;
|
||||
glTransformFeedbackVaryings(program_id, static_cast<GLsizei>(feedback_vars.size()),
|
||||
&varyings[0], GL_INTERLEAVED_ATTRIBS);
|
||||
}
|
||||
|
||||
if (separable_program) {
|
||||
glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||
}
|
||||
glAttachShader(program_id, vertex_shader_id);
|
||||
glAttachShader(program_id, fragment_shader_id);
|
||||
|
||||
glLinkProgram(program_id);
|
||||
|
||||
@@ -134,30 +85,13 @@ GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
|
||||
|
||||
// If the program linking failed at least one of the shaders was probably bad
|
||||
if (result == GL_FALSE) {
|
||||
if (vertex_shader) {
|
||||
LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
|
||||
}
|
||||
if (geometry_shader) {
|
||||
LOG_ERROR(Render_OpenGL, "Geometry shader:\n%s", geometry_shader);
|
||||
}
|
||||
if (fragment_shader) {
|
||||
LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
|
||||
}
|
||||
LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
|
||||
LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
|
||||
}
|
||||
ASSERT_MSG(result == GL_TRUE, "Shader not linked");
|
||||
|
||||
if (vertex_shader) {
|
||||
glDetachShader(program_id, vertex_shader_id);
|
||||
glDeleteShader(vertex_shader_id);
|
||||
}
|
||||
if (geometry_shader) {
|
||||
glDetachShader(program_id, geometry_shader_id);
|
||||
glDeleteShader(geometry_shader_id);
|
||||
}
|
||||
if (fragment_shader) {
|
||||
glDetachShader(program_id, fragment_shader_id);
|
||||
glDeleteShader(fragment_shader_id);
|
||||
}
|
||||
glDeleteShader(vertex_shader_id);
|
||||
glDeleteShader(fragment_shader_id);
|
||||
|
||||
return program_id;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace GLShader {
|
||||
@@ -12,12 +11,9 @@ namespace GLShader {
|
||||
/**
|
||||
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
|
||||
* @param vertex_shader String of the GLSL vertex shader program
|
||||
* @param geometry_shader String of the GLSL geometry shader program
|
||||
* @param fragment_shader String of the GLSL fragment shader program
|
||||
* @returns Handle of the newly created OpenGL shader object
|
||||
*/
|
||||
GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
|
||||
const char* fragment_shader, const std::vector<const char*>& feedback_vars = {},
|
||||
bool separable_program = false);
|
||||
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader);
|
||||
|
||||
} // namespace GLShader
|
||||
|
||||
@@ -33,7 +33,7 @@ OpenGLState::OpenGLState() {
|
||||
stencil.action_depth_pass = GL_KEEP;
|
||||
stencil.action_stencil_fail = GL_KEEP;
|
||||
|
||||
blend.enabled = true;
|
||||
blend.enabled = false;
|
||||
blend.rgb_equation = GL_FUNC_ADD;
|
||||
blend.a_equation = GL_FUNC_ADD;
|
||||
blend.src_rgb_func = GL_ONE;
|
||||
@@ -68,18 +68,6 @@ OpenGLState::OpenGLState() {
|
||||
draw.vertex_buffer = 0;
|
||||
draw.uniform_buffer = 0;
|
||||
draw.shader_program = 0;
|
||||
draw.program_pipeline = 0;
|
||||
|
||||
scissor.enabled = false;
|
||||
scissor.x = 0;
|
||||
scissor.y = 0;
|
||||
scissor.width = 0;
|
||||
scissor.height = 0;
|
||||
|
||||
viewport.x = 0;
|
||||
viewport.y = 0;
|
||||
viewport.width = 0;
|
||||
viewport.height = 0;
|
||||
|
||||
clip_distance = {};
|
||||
}
|
||||
@@ -160,6 +148,9 @@ void OpenGLState::Apply() const {
|
||||
if (blend.enabled != cur_state.blend.enabled) {
|
||||
if (blend.enabled) {
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
cur_state.logic_op = GL_COPY;
|
||||
glLogicOp(cur_state.logic_op);
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
@@ -205,7 +196,7 @@ void OpenGLState::Apply() const {
|
||||
// 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);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);
|
||||
}
|
||||
|
||||
// Fog LUT
|
||||
@@ -272,31 +263,6 @@ void OpenGLState::Apply() const {
|
||||
glUseProgram(draw.shader_program);
|
||||
}
|
||||
|
||||
// Program pipeline
|
||||
if (draw.program_pipeline != cur_state.draw.program_pipeline) {
|
||||
glBindProgramPipeline(draw.program_pipeline);
|
||||
}
|
||||
|
||||
// Scissor test
|
||||
if (scissor.enabled != cur_state.scissor.enabled) {
|
||||
if (scissor.enabled) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
|
||||
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
|
||||
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
|
||||
}
|
||||
|
||||
if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
|
||||
viewport.width != cur_state.viewport.width ||
|
||||
viewport.height != cur_state.viewport.height) {
|
||||
glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
||||
}
|
||||
|
||||
// Clip distance
|
||||
for (size_t i = 0; i < clip_distance.size(); ++i) {
|
||||
if (clip_distance[i] != cur_state.clip_distance[i]) {
|
||||
@@ -311,75 +277,62 @@ void OpenGLState::Apply() const {
|
||||
cur_state = *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetTexture(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
void OpenGLState::ResetTexture(GLuint handle) {
|
||||
for (auto& unit : cur_state.texture_units) {
|
||||
if (unit.texture_2d == handle) {
|
||||
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;
|
||||
if (cur_state.lighting_lut.texture_buffer == handle)
|
||||
cur_state.lighting_lut.texture_buffer = 0;
|
||||
if (cur_state.fog_lut.texture_buffer == handle)
|
||||
cur_state.fog_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_noise_lut.texture_buffer == handle)
|
||||
cur_state.proctex_noise_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_color_map.texture_buffer == handle)
|
||||
cur_state.proctex_color_map.texture_buffer = 0;
|
||||
if (cur_state.proctex_alpha_map.texture_buffer == handle)
|
||||
cur_state.proctex_alpha_map.texture_buffer = 0;
|
||||
if (cur_state.proctex_lut.texture_buffer == handle)
|
||||
cur_state.proctex_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_diff_lut.texture_buffer == handle)
|
||||
cur_state.proctex_diff_lut.texture_buffer = 0;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
void OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : cur_state.texture_units) {
|
||||
if (unit.sampler == handle) {
|
||||
unit.sampler = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (draw.shader_program == handle) {
|
||||
draw.shader_program = 0;
|
||||
void OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (cur_state.draw.shader_program == handle) {
|
||||
cur_state.draw.shader_program = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
|
||||
if (draw.program_pipeline == handle) {
|
||||
draw.program_pipeline = 0;
|
||||
void OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (cur_state.draw.vertex_buffer == handle) {
|
||||
cur_state.draw.vertex_buffer = 0;
|
||||
}
|
||||
if (cur_state.draw.uniform_buffer == handle) {
|
||||
cur_state.draw.uniform_buffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (draw.vertex_buffer == handle) {
|
||||
draw.vertex_buffer = 0;
|
||||
void OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (cur_state.draw.vertex_array == handle) {
|
||||
cur_state.draw.vertex_array = 0;
|
||||
}
|
||||
if (draw.uniform_buffer == handle) {
|
||||
draw.uniform_buffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (draw.vertex_array == handle) {
|
||||
draw.vertex_array = 0;
|
||||
void OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||
if (cur_state.draw.read_framebuffer == handle) {
|
||||
cur_state.draw.read_framebuffer = 0;
|
||||
}
|
||||
if (cur_state.draw.draw_framebuffer == handle) {
|
||||
cur_state.draw.draw_framebuffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||
if (draw.read_framebuffer == handle) {
|
||||
draw.read_framebuffer = 0;
|
||||
}
|
||||
if (draw.draw_framebuffer == handle) {
|
||||
draw.draw_framebuffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -122,44 +122,27 @@ public:
|
||||
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
|
||||
GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
|
||||
GLuint shader_program; // GL_CURRENT_PROGRAM
|
||||
GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
|
||||
} draw;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_SCISSOR_TEST
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} scissor;
|
||||
|
||||
struct {
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} viewport;
|
||||
|
||||
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
|
||||
|
||||
OpenGLState();
|
||||
|
||||
/// Get the currently active OpenGL state
|
||||
static OpenGLState GetCurState() {
|
||||
static const OpenGLState& GetCurState() {
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply() const;
|
||||
|
||||
/// Resets any references to the given resource
|
||||
OpenGLState& ResetTexture(GLuint handle);
|
||||
OpenGLState& ResetSampler(GLuint handle);
|
||||
OpenGLState& ResetProgram(GLuint handle);
|
||||
OpenGLState& ResetPipeline(GLuint handle);
|
||||
OpenGLState& ResetBuffer(GLuint handle);
|
||||
OpenGLState& ResetVertexArray(GLuint handle);
|
||||
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||
/// Resets and unbinds any references to the given resource in the current OpenGL state
|
||||
static void ResetTexture(GLuint handle);
|
||||
static void ResetSampler(GLuint handle);
|
||||
static void ResetProgram(GLuint handle);
|
||||
static void ResetBuffer(GLuint handle);
|
||||
static void ResetVertexArray(GLuint handle);
|
||||
static void ResetFramebuffer(GLuint handle);
|
||||
|
||||
private:
|
||||
static OpenGLState cur_state;
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
class OrphanBuffer : public OGLStreamBuffer {
|
||||
public:
|
||||
explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {}
|
||||
~OrphanBuffer() override;
|
||||
|
||||
private:
|
||||
void Create(size_t size, size_t sync_subdivide) override;
|
||||
void Release() override;
|
||||
|
||||
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override;
|
||||
void Unmap() override;
|
||||
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
class StorageBuffer : public OGLStreamBuffer {
|
||||
public:
|
||||
explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {}
|
||||
~StorageBuffer() override;
|
||||
|
||||
private:
|
||||
void Create(size_t size, size_t sync_subdivide) override;
|
||||
void Release() override;
|
||||
|
||||
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override;
|
||||
void Unmap() override;
|
||||
|
||||
struct Fence {
|
||||
OGLSync sync;
|
||||
size_t offset;
|
||||
};
|
||||
std::deque<Fence> head;
|
||||
std::deque<Fence> tail;
|
||||
|
||||
u8* mapped_ptr;
|
||||
};
|
||||
|
||||
OGLStreamBuffer::OGLStreamBuffer(GLenum target) {
|
||||
gl_target = target;
|
||||
}
|
||||
|
||||
GLuint OGLStreamBuffer::GetHandle() const {
|
||||
return gl_buffer.handle;
|
||||
}
|
||||
|
||||
std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) {
|
||||
if (storage_buffer) {
|
||||
return std::make_unique<StorageBuffer>(target);
|
||||
}
|
||||
return std::make_unique<OrphanBuffer>(target);
|
||||
}
|
||||
|
||||
OrphanBuffer::~OrphanBuffer() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) {
|
||||
buffer_pos = 0;
|
||||
buffer_size = size;
|
||||
data.resize(buffer_size);
|
||||
|
||||
if (gl_buffer.handle == 0) {
|
||||
gl_buffer.Create();
|
||||
glBindBuffer(gl_target, gl_buffer.handle);
|
||||
}
|
||||
|
||||
glBufferData(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
void OrphanBuffer::Release() {
|
||||
gl_buffer.Release();
|
||||
}
|
||||
|
||||
std::pair<u8*, GLintptr> OrphanBuffer::Map(size_t size, size_t alignment) {
|
||||
buffer_pos = Common::AlignUp(buffer_pos, alignment);
|
||||
|
||||
if (buffer_pos + size > buffer_size) {
|
||||
Create(std::max(buffer_size, size), 0);
|
||||
}
|
||||
|
||||
mapped_size = size;
|
||||
return std::make_pair(&data[buffer_pos], static_cast<GLintptr>(buffer_pos));
|
||||
}
|
||||
|
||||
void OrphanBuffer::Unmap() {
|
||||
glBufferSubData(gl_target, static_cast<GLintptr>(buffer_pos),
|
||||
static_cast<GLsizeiptr>(mapped_size), &data[buffer_pos]);
|
||||
buffer_pos += mapped_size;
|
||||
}
|
||||
|
||||
StorageBuffer::~StorageBuffer() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void StorageBuffer::Create(size_t size, size_t sync_subdivide) {
|
||||
if (gl_buffer.handle != 0)
|
||||
return;
|
||||
|
||||
buffer_pos = 0;
|
||||
buffer_size = size;
|
||||
buffer_sync_subdivide = std::max<size_t>(sync_subdivide, 1);
|
||||
|
||||
gl_buffer.Create();
|
||||
glBindBuffer(gl_target, gl_buffer.handle);
|
||||
|
||||
glBufferStorage(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
mapped_ptr = reinterpret_cast<u8*>(
|
||||
glMapBufferRange(gl_target, 0, static_cast<GLsizeiptr>(buffer_size),
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
|
||||
}
|
||||
|
||||
void StorageBuffer::Release() {
|
||||
if (gl_buffer.handle == 0)
|
||||
return;
|
||||
|
||||
glUnmapBuffer(gl_target);
|
||||
|
||||
gl_buffer.Release();
|
||||
head.clear();
|
||||
tail.clear();
|
||||
}
|
||||
|
||||
std::pair<u8*, GLintptr> StorageBuffer::Map(size_t size, size_t alignment) {
|
||||
ASSERT(size <= buffer_size);
|
||||
|
||||
OGLSync sync;
|
||||
|
||||
buffer_pos = Common::AlignUp(buffer_pos, alignment);
|
||||
size_t effective_offset = Common::AlignDown(buffer_pos, buffer_sync_subdivide);
|
||||
|
||||
if (!head.empty() &&
|
||||
(effective_offset > head.back().offset || buffer_pos + size > buffer_size)) {
|
||||
ASSERT(head.back().sync.handle == 0);
|
||||
head.back().sync.Create();
|
||||
}
|
||||
|
||||
if (buffer_pos + size > buffer_size) {
|
||||
if (!tail.empty()) {
|
||||
std::swap(sync, tail.back().sync);
|
||||
tail.clear();
|
||||
}
|
||||
std::swap(tail, head);
|
||||
buffer_pos = 0;
|
||||
effective_offset = 0;
|
||||
}
|
||||
|
||||
while (!tail.empty() && buffer_pos + size > tail.front().offset) {
|
||||
std::swap(sync, tail.front().sync);
|
||||
tail.pop_front();
|
||||
}
|
||||
|
||||
if (sync.handle != 0) {
|
||||
glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||
sync.Release();
|
||||
}
|
||||
|
||||
if (head.empty() || effective_offset > head.back().offset) {
|
||||
head.emplace_back();
|
||||
head.back().offset = effective_offset;
|
||||
}
|
||||
|
||||
mapped_size = size;
|
||||
return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos));
|
||||
}
|
||||
|
||||
void StorageBuffer::Unmap() {
|
||||
glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos),
|
||||
static_cast<GLsizeiptr>(mapped_size));
|
||||
buffer_pos += mapped_size;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
class OGLStreamBuffer : private NonCopyable {
|
||||
public:
|
||||
explicit OGLStreamBuffer(GLenum target);
|
||||
virtual ~OGLStreamBuffer() = default;
|
||||
|
||||
public:
|
||||
static std::unique_ptr<OGLStreamBuffer> MakeBuffer(bool storage_buffer, GLenum target);
|
||||
|
||||
virtual void Create(size_t size, size_t sync_subdivide) = 0;
|
||||
virtual void Release() {}
|
||||
|
||||
GLuint GetHandle() const;
|
||||
|
||||
virtual std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) = 0;
|
||||
virtual void Unmap() = 0;
|
||||
|
||||
protected:
|
||||
OGLBuffer gl_buffer;
|
||||
GLenum gl_target;
|
||||
|
||||
size_t buffer_pos = 0;
|
||||
size_t buffer_size = 0;
|
||||
size_t buffer_sync_subdivide = 0;
|
||||
size_t mapped_size = 0;
|
||||
};
|
||||
@@ -318,7 +318,7 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||
0.0f);
|
||||
|
||||
// Link shaders and get variable locations
|
||||
shader.Create(vertex_shader, nullptr, fragment_shader);
|
||||
shader.Create(vertex_shader, fragment_shader);
|
||||
state.draw.shader_program = shader.handle;
|
||||
state.Apply();
|
||||
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
||||
|
||||
@@ -150,8 +150,8 @@ QString WaitTreeThread::GetText() const {
|
||||
case THREADSTATUS_READY:
|
||||
status = tr("ready");
|
||||
break;
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
status = tr("waiting for HLE return");
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
status = tr("waiting for address 0x%1").arg(thread.wait_address, 8, 16, QLatin1Char('0'));
|
||||
break;
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
status = tr("sleeping");
|
||||
@@ -180,7 +180,7 @@ QColor WaitTreeThread::GetColor() const {
|
||||
return QColor(Qt::GlobalColor::darkGreen);
|
||||
case THREADSTATUS_READY:
|
||||
return QColor(Qt::GlobalColor::darkBlue);
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
return QColor(Qt::GlobalColor::darkRed);
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
return QColor(Qt::GlobalColor::darkYellow);
|
||||
|
||||
Reference in New Issue
Block a user