Compare commits
54 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8660b13b52 | ||
|
|
f54414da98 | ||
|
|
df038aa0bf | ||
|
|
c02573feac | ||
|
|
b16e5c6a81 | ||
|
|
87f21657f8 | ||
|
|
e6df4b37db | ||
|
|
aee356bd10 | ||
|
|
cd7665218d | ||
|
|
e2cdf54177 | ||
|
|
e3a92b09ba | ||
|
|
e2db7a83f6 | ||
|
|
126270d963 | ||
|
|
e6a87428ae | ||
|
|
55e6296e71 | ||
|
|
1ce7942dc2 | ||
|
|
6b6287dda0 | ||
|
|
c74d24f841 | ||
|
|
4cb92b776c | ||
|
|
a55f112cb1 | ||
|
|
5cdc277dd2 | ||
|
|
74efdd6928 | ||
|
|
3825b703fa | ||
|
|
1efb81a61d | ||
|
|
3c26b7179d | ||
|
|
8c648b59cd | ||
|
|
f217d6c66f | ||
|
|
58d9078742 | ||
|
|
58857b9f46 | ||
|
|
c6eaf0b2cf | ||
|
|
693f78e6c2 | ||
|
|
898f0fa029 | ||
|
|
ff54287a73 | ||
|
|
882111c4f2 | ||
|
|
6486544e09 | ||
|
|
2dbfcd32d7 | ||
|
|
8440cef223 | ||
|
|
fd500d3da6 | ||
|
|
525492428d | ||
|
|
72b5c448cf | ||
|
|
03388c3071 | ||
|
|
353e1dd7e4 | ||
|
|
a215f63235 | ||
|
|
dc26601860 | ||
|
|
2a35a36251 | ||
|
|
c74f2555b6 | ||
|
|
fab3dd98fe | ||
|
|
c50393e066 | ||
|
|
a056d5ad8c | ||
|
|
98b143c2d6 | ||
|
|
370ab5df9b | ||
|
|
21959ddfef | ||
|
|
fd86cdb2e2 | ||
|
|
0984e9d601 |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 5e20e5a44a...990a569b7a
@@ -29,9 +29,14 @@ enum class ControlCommand : u32 {
|
||||
};
|
||||
|
||||
enum class CommandType : u32 {
|
||||
Invalid = 0,
|
||||
LegacyRequest = 1,
|
||||
Close = 2,
|
||||
LegacyControl = 3,
|
||||
Request = 4,
|
||||
Control = 5,
|
||||
RequestWithContext = 6,
|
||||
ControlWithContext = 7,
|
||||
Unspecified,
|
||||
};
|
||||
|
||||
|
||||
@@ -110,7 +110,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// Padding to align to 16 bytes
|
||||
rp.AlignWithPadding();
|
||||
|
||||
if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
|
||||
if (Session()->IsDomain() && ((command_header->type == IPC::CommandType::Request ||
|
||||
command_header->type == IPC::CommandType::RequestWithContext) ||
|
||||
!incoming)) {
|
||||
// If this is an incoming message, only CommandType "Request" has a domain header
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
|
||||
@@ -104,7 +104,6 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
|
||||
// There are no more threads waiting for the mutex, release it completely.
|
||||
if (thread == nullptr) {
|
||||
ASSERT(GetCurrentThread()->wait_mutex_threads.empty());
|
||||
Memory::Write32(address, 0);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -145,36 +145,6 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr
|
||||
return true;
|
||||
};
|
||||
|
||||
/// Wait for a kernel object to synchronize, timeout after the specified nanoseconds
|
||||
static ResultCode WaitSynchronization1(
|
||||
SharedPtr<WaitObject> object, Thread* thread, s64 nano_seconds = -1,
|
||||
std::function<Thread::WakeupCallback> wakeup_callback = DefaultThreadWakeupCallback) {
|
||||
|
||||
if (!object) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (object->ShouldWait(thread)) {
|
||||
if (nano_seconds == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
thread->wait_objects = {object};
|
||||
object->AddWaitingThread(thread);
|
||||
thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
|
||||
|
||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||
thread->WakeAfterDelay(nano_seconds);
|
||||
thread->wakeup_callback = wakeup_callback;
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
} else {
|
||||
object->Acquire(thread);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||
static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count,
|
||||
s64 nano_seconds) {
|
||||
@@ -232,7 +202,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||
thread->WakeAfterDelay(nano_seconds);
|
||||
thread->wakeup_callback = DefaultThreadWakeupCallback;
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
@@ -395,7 +365,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
|
||||
thread->SetPriority(priority);
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -552,6 +522,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
*out_handle = thread->guest_handle;
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
NGLOG_TRACE(Kernel_SVC,
|
||||
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
@@ -570,7 +541,10 @@ static ResultCode StartThread(Handle thread_handle) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
ASSERT(thread->status == THREADSTATUS_DORMANT);
|
||||
|
||||
thread->ResumeFromWait();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -634,61 +608,78 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
NGLOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
|
||||
condition_variable_addr, target);
|
||||
|
||||
u32 processed = 0;
|
||||
auto RetrieveWaitingThreads =
|
||||
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) {
|
||||
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
||||
auto& thread_list = scheduler->GetThreadList();
|
||||
|
||||
auto signal_process_wide_key = [&](size_t core_index) {
|
||||
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
||||
for (auto& thread : scheduler->GetThreadList()) {
|
||||
if (thread->condvar_wait_address != condition_variable_addr)
|
||||
continue;
|
||||
|
||||
// Only process up to 'target' threads, unless 'target' is -1, in which case process
|
||||
// them all.
|
||||
if (target != -1 && processed >= target)
|
||||
break;
|
||||
|
||||
// If the mutex is not yet acquired, acquire it.
|
||||
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
|
||||
|
||||
if (mutex_val == 0) {
|
||||
// We were able to acquire the mutex, resume this thread.
|
||||
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
|
||||
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
|
||||
thread->ResumeFromWait();
|
||||
|
||||
auto lock_owner = thread->lock_owner;
|
||||
if (lock_owner)
|
||||
lock_owner->RemoveMutexWaiter(thread);
|
||||
|
||||
thread->lock_owner = nullptr;
|
||||
thread->mutex_wait_address = 0;
|
||||
thread->condvar_wait_address = 0;
|
||||
thread->wait_handle = 0;
|
||||
} else {
|
||||
// Couldn't acquire the mutex, block the thread.
|
||||
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||
auto owner = g_handle_table.Get<Thread>(owner_handle);
|
||||
ASSERT(owner);
|
||||
ASSERT(thread->status != THREADSTATUS_RUNNING);
|
||||
thread->status = THREADSTATUS_WAIT_MUTEX;
|
||||
thread->wakeup_callback = nullptr;
|
||||
|
||||
// Signal that the mutex now has a waiting thread.
|
||||
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
|
||||
|
||||
owner->AddMutexWaiter(thread);
|
||||
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->condvar_wait_address == condvar_addr)
|
||||
waiting_threads.push_back(thread);
|
||||
}
|
||||
};
|
||||
|
||||
++processed;
|
||||
// Retrieve a list of all threads that are waiting for this condition variable.
|
||||
std::vector<SharedPtr<Thread>> waiting_threads;
|
||||
RetrieveWaitingThreads(0, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(1, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(2, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(3, waiting_threads, condition_variable_addr);
|
||||
// Sort them by priority, such that the highest priority ones come first.
|
||||
std::sort(waiting_threads.begin(), waiting_threads.end(),
|
||||
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
||||
return lhs->current_priority < rhs->current_priority;
|
||||
});
|
||||
|
||||
// Only process up to 'target' threads, unless 'target' is -1, in which case process
|
||||
// them all.
|
||||
size_t last = waiting_threads.size();
|
||||
if (target != -1)
|
||||
last = target;
|
||||
|
||||
// If there are no threads waiting on this condition variable, just exit
|
||||
if (last > waiting_threads.size())
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
for (size_t index = 0; index < last; ++index) {
|
||||
auto& thread = waiting_threads[index];
|
||||
|
||||
ASSERT(thread->condvar_wait_address == condition_variable_addr);
|
||||
|
||||
// If the mutex is not yet acquired, acquire it.
|
||||
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
|
||||
|
||||
if (mutex_val == 0) {
|
||||
// We were able to acquire the mutex, resume this thread.
|
||||
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
|
||||
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
|
||||
thread->ResumeFromWait();
|
||||
|
||||
auto lock_owner = thread->lock_owner;
|
||||
if (lock_owner)
|
||||
lock_owner->RemoveMutexWaiter(thread);
|
||||
|
||||
thread->lock_owner = nullptr;
|
||||
thread->mutex_wait_address = 0;
|
||||
thread->condvar_wait_address = 0;
|
||||
thread->wait_handle = 0;
|
||||
} else {
|
||||
// Couldn't acquire the mutex, block the thread.
|
||||
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||
auto owner = g_handle_table.Get<Thread>(owner_handle);
|
||||
ASSERT(owner);
|
||||
ASSERT(thread->status != THREADSTATUS_RUNNING);
|
||||
thread->status = THREADSTATUS_WAIT_MUTEX;
|
||||
thread->wakeup_callback = nullptr;
|
||||
|
||||
// Signal that the mutex now has a waiting thread.
|
||||
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
|
||||
|
||||
owner->AddMutexWaiter(thread);
|
||||
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
}
|
||||
};
|
||||
|
||||
signal_process_wide_key(0);
|
||||
signal_process_wide_key(1);
|
||||
signal_process_wide_key(2);
|
||||
signal_process_wide_key(3);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -47,9 +47,12 @@ enum class GetInfoType : u64 {
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
};
|
||||
|
||||
void CallSVC(u32 immediate);
|
||||
|
||||
@@ -32,7 +32,8 @@ enum class ErrorModule : u32 {
|
||||
Common = 0,
|
||||
Kernel = 1,
|
||||
FS = 2,
|
||||
NvidiaTransferMemory = 3,
|
||||
OS = 3, // used for Memory, Thread, Mutex, Nvidia
|
||||
HTCS = 4,
|
||||
NCM = 5,
|
||||
DD = 6,
|
||||
LR = 8,
|
||||
@@ -42,41 +43,80 @@ enum class ErrorModule : u32 {
|
||||
PM = 15,
|
||||
NS = 16,
|
||||
HTC = 18,
|
||||
NCMContent = 20,
|
||||
SM = 21,
|
||||
RO = 22,
|
||||
SDMMC = 24,
|
||||
OVLN = 25,
|
||||
SPL = 26,
|
||||
ETHC = 100,
|
||||
I2C = 101,
|
||||
GPIO = 102,
|
||||
UART = 103,
|
||||
Settings = 105,
|
||||
WLAN = 107,
|
||||
XCD = 108,
|
||||
NIFM = 110,
|
||||
Display = 114,
|
||||
NTC = 116,
|
||||
Hwopus = 111,
|
||||
Bluetooth = 113,
|
||||
VI = 114,
|
||||
NFP = 115,
|
||||
Time = 116,
|
||||
FGM = 117,
|
||||
PCIE = 120,
|
||||
OE = 118,
|
||||
PCIe = 120,
|
||||
Friends = 121,
|
||||
BCAT = 122,
|
||||
SSL = 123,
|
||||
Account = 124,
|
||||
News = 125,
|
||||
Mii = 126,
|
||||
NFC = 127,
|
||||
AM = 128,
|
||||
PlayReport = 129,
|
||||
AHID = 130,
|
||||
Qlaunch = 132,
|
||||
PCV = 133,
|
||||
OMM = 134,
|
||||
BPC = 135,
|
||||
PSM = 136,
|
||||
NIM = 137,
|
||||
PSC = 138,
|
||||
TC = 139,
|
||||
USB = 140,
|
||||
NSD = 141,
|
||||
PCTL = 142,
|
||||
BTM = 143,
|
||||
ETicket = 145,
|
||||
NGC = 146,
|
||||
ERPT = 147,
|
||||
APM = 148,
|
||||
Profiler = 150,
|
||||
ErrorUpload = 151,
|
||||
Audio = 153,
|
||||
NPNS = 154,
|
||||
NPNSHTTPSTREAM = 155,
|
||||
ARP = 157,
|
||||
BOOT = 158,
|
||||
NFC = 161,
|
||||
SWKBD = 158,
|
||||
BOOT = 159,
|
||||
NFCMifare = 161,
|
||||
UserlandAssert = 162,
|
||||
Fatal = 163,
|
||||
NIMShop = 164,
|
||||
SPSM = 165,
|
||||
BGTC = 167,
|
||||
UserlandCrash = 168,
|
||||
HID = 203,
|
||||
SREPO = 180,
|
||||
Dauth = 181,
|
||||
HID = 202,
|
||||
LDN = 203,
|
||||
Irsensor = 205,
|
||||
Capture = 206,
|
||||
TC = 651,
|
||||
Manu = 208,
|
||||
ATK = 209,
|
||||
GRC = 212,
|
||||
Migration = 216,
|
||||
MigrationLdcServ = 217,
|
||||
GeneralWebApplet = 800,
|
||||
WifiWebAuthApplet = 809,
|
||||
WhitelistedApplet = 810,
|
||||
|
||||
@@ -74,7 +74,40 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {}
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLastForegroundCaptureImage"},
|
||||
{1, nullptr, "UpdateLastForegroundCaptureImage"},
|
||||
{2, nullptr, "GetLastApplicationCaptureImage"},
|
||||
{3, nullptr, "GetCallerAppletCaptureImage"},
|
||||
{4, nullptr, "UpdateCallerAppletCaptureImage"},
|
||||
{5, nullptr, "GetLastForegroundCaptureImageEx"},
|
||||
{6, nullptr, "GetLastApplicationCaptureImageEx"},
|
||||
{7, nullptr, "GetCallerAppletCaptureImageEx"},
|
||||
{8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+
|
||||
{9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+
|
||||
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
|
||||
{11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
|
||||
{12, nullptr, "AcquireLastForegroundCaptureBuffer"},
|
||||
{13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
|
||||
{14, nullptr, "AcquireCallerAppletCaptureBuffer"},
|
||||
{15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
|
||||
{16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
|
||||
{17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
|
||||
{18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
|
||||
// 2.0.0+
|
||||
{20, nullptr, "ClearCaptureBuffer"},
|
||||
{21, nullptr, "ClearAppletTransitionBuffer"},
|
||||
// 4.0.0+
|
||||
{22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
|
||||
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
|
||||
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
|
||||
|
||||
|
||||
@@ -18,10 +18,25 @@ class NVFlinger;
|
||||
|
||||
namespace AM {
|
||||
|
||||
// TODO: Add more languages
|
||||
enum SystemLanguage {
|
||||
Japanese = 0,
|
||||
English = 1,
|
||||
English = 1, // en-US
|
||||
French = 2,
|
||||
German = 3,
|
||||
Italian = 4,
|
||||
Spanish = 5,
|
||||
Chinese = 6,
|
||||
Korean = 7,
|
||||
Dutch = 8,
|
||||
Portuguese = 9,
|
||||
Russian = 10,
|
||||
Taiwanese = 11,
|
||||
BritishEnglish = 12, // en-GB
|
||||
CanadianFrench = 13,
|
||||
LatinAmericanSpanish = 14, // es-419
|
||||
// 4.0.0+
|
||||
SimplifiedChinese = 15,
|
||||
TraditionalChinese = 16,
|
||||
};
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
|
||||
@@ -98,7 +98,7 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,8 @@ public:
|
||||
|
||||
AudInU::AudInU() : ServiceFramework("audin:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListAudioIns"},
|
||||
{1, nullptr, "OpenAudioIn"},
|
||||
{3, nullptr, "OpenAudioInAuto"},
|
||||
{4, nullptr, "ListAudioInsAuto"},
|
||||
{0, nullptr, "ListAudioIns"}, {1, nullptr, "OpenAudioIn"}, {2, nullptr, "Unknown"},
|
||||
{3, nullptr, "OpenAudioInAuto"}, {4, nullptr, "ListAudioInsAuto"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -196,10 +196,10 @@ void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||
static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{0x00000002, nullptr, "ListAudioOutsAuto"},
|
||||
{0x00000003, nullptr, "OpenAudioOutAuto"}};
|
||||
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{2, nullptr, "ListAudioOutsAuto"},
|
||||
{3, nullptr, "OpenAudioOutAuto"}};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
{4, nullptr, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedFinalOutputRecorderBuffer"},
|
||||
{6, nullptr, "ContainsFinalOutputRecorderBuffer"},
|
||||
{7, nullptr, "Unknown"},
|
||||
{8, nullptr, "AppendFinalOutputRecorderBufferAuto"},
|
||||
{9, nullptr, "GetReleasedFinalOutputRecorderBufferAuto"},
|
||||
};
|
||||
@@ -30,7 +31,7 @@ public:
|
||||
|
||||
AudRecU::AudRecU() : ServiceFramework("audrec:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, nullptr, "OpenFinalOutputRecorder"},
|
||||
{0, nullptr, "OpenFinalOutputRecorder"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -156,19 +157,20 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
public:
|
||||
IAudioDevice() : ServiceFramework("IAudioDevice") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
{0x2, nullptr, "GetAudioDeviceOutputVolume"},
|
||||
{0x3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{0x4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{0x5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{0x6, &IAudioDevice::ListAudioDeviceName,
|
||||
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
{2, nullptr, "GetAudioDeviceOutputVolume"},
|
||||
{3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{6, &IAudioDevice::ListAudioDeviceName,
|
||||
"ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto
|
||||
{0x7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{0x8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{0xa, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{0xb, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{0xc, nullptr, "QueryAudioDeviceOutputEvent"}};
|
||||
{7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{12, nullptr, "QueryAudioDeviceOutputEvent"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
buffer_event =
|
||||
@@ -255,12 +257,62 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<WorkerBufferParameters>();
|
||||
|
||||
u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40);
|
||||
buffer_sz += params.unknownC * 1024;
|
||||
buffer_sz += 0x940 * (params.unknownC + 1);
|
||||
buffer_sz += 0x3F0 * params.voice_count;
|
||||
buffer_sz += Common::AlignUp(8 * (params.unknownC + 1), 0x10);
|
||||
buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
|
||||
buffer_sz +=
|
||||
Common::AlignUp((0x3C0 * (params.sink_count + params.unknownC) + 4 * params.sample_count) *
|
||||
(params.unknown8 + 6),
|
||||
0x40);
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) {
|
||||
u32 count = params.unknownC + 1;
|
||||
u64 node_count = Common::AlignUp(count, 0x40);
|
||||
u64 node_state_buffer_sz =
|
||||
4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
|
||||
u64 edge_matrix_buffer_sz = 0;
|
||||
node_count = Common::AlignUp(count * count, 0x40);
|
||||
if (node_count >> 31 != 0) {
|
||||
edge_matrix_buffer_sz = (node_count | 7) / 8;
|
||||
} else {
|
||||
edge_matrix_buffer_sz = node_count / 8;
|
||||
}
|
||||
buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
|
||||
}
|
||||
|
||||
buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) {
|
||||
buffer_sz += 0xE0 * params.unknown2c;
|
||||
buffer_sz += 0x20 * params.splitter_count;
|
||||
buffer_sz += Common::AlignUp(4 * params.unknown2c, 0x10);
|
||||
}
|
||||
buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
|
||||
u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
|
||||
((params.voice_count * 256) | 0x40);
|
||||
|
||||
if (params.unknown1c >= 1) {
|
||||
output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
|
||||
16 * params.voice_count + 16) +
|
||||
0x658) *
|
||||
(params.unknown1c + 1) +
|
||||
0xc0,
|
||||
0x40) +
|
||||
output_sz;
|
||||
}
|
||||
output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0x4000);
|
||||
rb.Push<u64>(output_sz);
|
||||
|
||||
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
NGLOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
@@ -272,4 +324,14 @@ void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
|
||||
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
|
||||
switch (feature) {
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -21,6 +21,31 @@ private:
|
||||
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDevice(Kernel::HLERequestContext& ctx);
|
||||
|
||||
struct WorkerBufferParameters {
|
||||
u32_le sample_rate;
|
||||
u32_le sample_count;
|
||||
u32_le unknown8;
|
||||
u32_le unknownC;
|
||||
u32_le voice_count;
|
||||
u32_le sink_count;
|
||||
u32_le effect_count;
|
||||
u32_le unknown1c;
|
||||
u8 unknown20;
|
||||
u8 padding1[3];
|
||||
u32_le splitter_count;
|
||||
u32_le unknown2c;
|
||||
u8 padding2[4];
|
||||
u32_le magic;
|
||||
};
|
||||
static_assert(sizeof(WorkerBufferParameters) == 52,
|
||||
"WorkerBufferParameters is an invalid size");
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
Splitter,
|
||||
};
|
||||
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -11,19 +11,19 @@ namespace Service::Audio {
|
||||
|
||||
CodecCtl::CodecCtl() : ServiceFramework("codecctl") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, nullptr, "InitializeCodecController"},
|
||||
{0x00000001, nullptr, "FinalizeCodecController"},
|
||||
{0x00000002, nullptr, "SleepCodecController"},
|
||||
{0x00000003, nullptr, "WakeCodecController"},
|
||||
{0x00000004, nullptr, "SetCodecVolume"},
|
||||
{0x00000005, nullptr, "GetCodecVolumeMax"},
|
||||
{0x00000006, nullptr, "GetCodecVolumeMin"},
|
||||
{0x00000007, nullptr, "SetCodecActiveTarget"},
|
||||
{0x00000008, nullptr, "Unknown"},
|
||||
{0x00000009, nullptr, "BindCodecHeadphoneMicJackInterrupt"},
|
||||
{0x00000010, nullptr, "IsCodecHeadphoneMicJackInserted"},
|
||||
{0x00000011, nullptr, "ClearCodecHeadphoneMicJackInterrupt"},
|
||||
{0x00000012, nullptr, "IsCodecDeviceRequested"},
|
||||
{0, nullptr, "InitializeCodecController"},
|
||||
{1, nullptr, "FinalizeCodecController"},
|
||||
{2, nullptr, "SleepCodecController"},
|
||||
{3, nullptr, "WakeCodecController"},
|
||||
{4, nullptr, "SetCodecVolume"},
|
||||
{5, nullptr, "GetCodecVolumeMax"},
|
||||
{6, nullptr, "GetCodecVolumeMin"},
|
||||
{7, nullptr, "SetCodecActiveTarget"},
|
||||
{8, nullptr, "GetCodecActiveTarget"},
|
||||
{9, nullptr, "BindCodecHeadphoneMicJackInterrupt"},
|
||||
{10, nullptr, "IsCodecHeadphoneMicJackInserted"},
|
||||
{11, nullptr, "ClearCodecHeadphoneMicJackInterrupt"},
|
||||
{12, nullptr, "IsCodecDeviceRequested"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -329,6 +329,7 @@ public:
|
||||
{130, nullptr, "SwapNpadAssignment"},
|
||||
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
|
||||
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
|
||||
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
|
||||
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
|
||||
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
|
||||
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
|
||||
@@ -336,12 +337,41 @@ public:
|
||||
{204, nullptr, "PermitVibration"},
|
||||
{205, nullptr, "IsVibrationPermitted"},
|
||||
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
|
||||
{207, nullptr, "SendVibrationGcErmCommand"},
|
||||
{208, nullptr, "GetActualVibrationGcErmCommand"},
|
||||
{209, nullptr, "BeginPermitVibrationSession"},
|
||||
{210, nullptr, "EndPermitVibrationSession"},
|
||||
{300, nullptr, "ActivateConsoleSixAxisSensor"},
|
||||
{301, nullptr, "StartConsoleSixAxisSensor"},
|
||||
{302, nullptr, "StopConsoleSixAxisSensor"},
|
||||
{303, nullptr, "ActivateSevenSixAxisSensor"},
|
||||
{304, nullptr, "StartSevenSixAxisSensor"},
|
||||
{305, nullptr, "StopSevenSixAxisSensor"},
|
||||
{306, nullptr, "InitializeSevenSixAxisSensor"},
|
||||
{307, nullptr, "FinalizeSevenSixAxisSensor"},
|
||||
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
|
||||
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
|
||||
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
|
||||
{401, nullptr, "EnableUsbFullKeyController"},
|
||||
{402, nullptr, "IsUsbFullKeyControllerConnected"},
|
||||
{403, nullptr, "HasBattery"},
|
||||
{404, nullptr, "HasLeftRightBattery"},
|
||||
{405, nullptr, "GetNpadInterfaceType"},
|
||||
{406, nullptr, "GetNpadLeftRightInterfaceType"},
|
||||
{500, nullptr, "GetPalmaConnectionHandle"},
|
||||
{501, nullptr, "InitializePalma"},
|
||||
{502, nullptr, "AcquirePalmaOperationCompleteEvent"},
|
||||
{503, nullptr, "GetPalmaOperationInfo"},
|
||||
{504, nullptr, "PlayPalmaActivity"},
|
||||
{505, nullptr, "SetPalmaFrModeType"},
|
||||
{506, nullptr, "ReadPalmaStep"},
|
||||
{507, nullptr, "EnablePalmaStep"},
|
||||
{508, nullptr, "SuspendPalmaStep"},
|
||||
{509, nullptr, "ResetPalmaStep"},
|
||||
{510, nullptr, "ReadPalmaApplicationSection"},
|
||||
{511, nullptr, "WritePalmaApplicationSection"},
|
||||
{512, nullptr, "ReadPalmaUniqueCode"},
|
||||
{513, nullptr, "SetPalmaUniqueCodeInvalid"},
|
||||
{1000, nullptr, "SetNpadCommunicationMode"},
|
||||
{1001, nullptr, "GetNpadCommunicationMode"},
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::HID {
|
||||
// Begin enums and output structs
|
||||
|
||||
constexpr u32 HID_NUM_ENTRIES = 17;
|
||||
constexpr u32 HID_NUM_LAYOUTS = 7;
|
||||
constexpr u32 HID_NUM_LAYOUTS = 2;
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x8000;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x8000;
|
||||
|
||||
|
||||
@@ -17,30 +17,30 @@ public:
|
||||
IUser() : ServiceFramework("IUser") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::Initialize, "Initialize"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
{13, nullptr, "Unknown13"},
|
||||
{14, nullptr, "Unknown14"},
|
||||
{15, nullptr, "Unknown15"},
|
||||
{16, nullptr, "Unknown16"},
|
||||
{17, nullptr, "Unknown17"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{1, nullptr, "Finalize"},
|
||||
{2, nullptr, "ListDevices"},
|
||||
{3, nullptr, "StartDetection"},
|
||||
{4, nullptr, "StopDetection"},
|
||||
{5, nullptr, "Mount"},
|
||||
{6, nullptr, "Unmount"},
|
||||
{7, nullptr, "OpenApplicationArea"},
|
||||
{8, nullptr, "GetApplicationArea"},
|
||||
{9, nullptr, "SetApplicationArea"},
|
||||
{10, nullptr, "Flush"},
|
||||
{11, nullptr, "Restore"},
|
||||
{12, nullptr, "CreateApplicationArea"},
|
||||
{13, nullptr, "GetTagInfo"},
|
||||
{14, nullptr, "GetRegisterInfo"},
|
||||
{15, nullptr, "GetCommonInfo"},
|
||||
{16, nullptr, "GetModelInfo"},
|
||||
{17, nullptr, "AttachActivateEvent"},
|
||||
{18, nullptr, "AttachDeactivateEvent"},
|
||||
{19, nullptr, "GetState"},
|
||||
{20, nullptr, "GetDeviceState"},
|
||||
{21, nullptr, "GetNpadId"},
|
||||
{22, nullptr, "GetApplicationArea2"},
|
||||
{23, nullptr, "AttachAvailabilityChangeEvent"},
|
||||
{24, nullptr, "RecreateApplicationArea"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vecto
|
||||
return BindChannel(input, output);
|
||||
case IoctlCommand::IocGetVaRegionsCommand:
|
||||
return GetVARegions(input, output);
|
||||
case IoctlCommand::IocUnmapBufferCommand:
|
||||
return UnmapBuffer(input, output);
|
||||
}
|
||||
|
||||
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
|
||||
@@ -125,6 +127,37 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size);
|
||||
}
|
||||
|
||||
// Create a new mapping entry for this operation.
|
||||
ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(),
|
||||
"Offset is already mapped");
|
||||
|
||||
BufferMapping mapping{};
|
||||
mapping.nvmap_handle = params.nvmap_handle;
|
||||
mapping.offset = params.offset;
|
||||
mapping.size = object->size;
|
||||
|
||||
buffer_mappings[params.offset] = mapping;
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlUnmapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
|
||||
NGLOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
|
||||
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
|
||||
auto itr = buffer_mappings.find(params.offset);
|
||||
|
||||
ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
|
||||
|
||||
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
|
||||
|
||||
buffer_mappings.erase(itr->second.offset);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -30,6 +31,7 @@ private:
|
||||
IocMapBufferExCommand = 0xC0284106,
|
||||
IocBindChannelCommand = 0x40044101,
|
||||
IocGetVaRegionsCommand = 0xC0404108,
|
||||
IocUnmapBufferCommand = 0xC0084105,
|
||||
};
|
||||
|
||||
struct IoctlInitalizeEx {
|
||||
@@ -76,6 +78,11 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
|
||||
|
||||
struct IoctlUnmapBuffer {
|
||||
u64_le offset;
|
||||
};
|
||||
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
|
||||
|
||||
struct IoctlBindChannel {
|
||||
u32_le fd;
|
||||
};
|
||||
@@ -98,12 +105,22 @@ private:
|
||||
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
|
||||
"IoctlGetVaRegions is incorrect size");
|
||||
|
||||
struct BufferMapping {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 nvmap_handle;
|
||||
};
|
||||
|
||||
/// Map containing the nvmap object mappings in GPU memory.
|
||||
std::unordered_map<u64, BufferMapping> buffer_mappings;
|
||||
|
||||
u32 channel{};
|
||||
|
||||
u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
|
||||
@@ -26,12 +26,64 @@ private:
|
||||
IocSyncptIncrCommand = 0x40040015,
|
||||
IocSyncptWaitCommand = 0xC00C0016,
|
||||
IocModuleMutexCommand = 0x40080017,
|
||||
IocModuleRegRDWRCommand = 0xC008010E,
|
||||
IocModuleRegRDWRCommand = 0xC0180018,
|
||||
IocSyncptWaitexCommand = 0xC0100019,
|
||||
IocSyncptReadMaxCommand = 0xC008001A,
|
||||
IocCtrlEventWaitCommand = 0xC010001D,
|
||||
IocGetConfigCommand = 0xC183001B,
|
||||
IocCtrlEventSignalCommand = 0xC004001C,
|
||||
IocCtrlEventWaitCommand = 0xC010001D,
|
||||
IocCtrlEventWaitAsyncCommand = 0xC010001E,
|
||||
IocCtrlEventRegisterCommand = 0xC004001F,
|
||||
IocCtrlEventUnregisterCommand = 0xC0040020,
|
||||
IocCtrlEventKillCommand = 0x40080021,
|
||||
};
|
||||
struct IocSyncptReadParams {
|
||||
u32_le id;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
|
||||
|
||||
struct IocSyncptIncrParams {
|
||||
u32_le id;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
|
||||
|
||||
struct IocSyncptWaitParams {
|
||||
u32_le id;
|
||||
u32_le thresh;
|
||||
s32_le timeout;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
|
||||
|
||||
struct IocModuleMutexParams {
|
||||
u32_le id;
|
||||
u32_le lock; // (0 = unlock and 1 = lock)
|
||||
};
|
||||
static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
|
||||
|
||||
struct IocModuleRegRDWRParams {
|
||||
u32_le id;
|
||||
u32_le num_offsets;
|
||||
u32_le block_size;
|
||||
u32_le offsets;
|
||||
u32_le values;
|
||||
u32_le write;
|
||||
};
|
||||
static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
|
||||
|
||||
struct IocSyncptWaitexParams {
|
||||
u32_le id;
|
||||
u32_le thresh;
|
||||
s32_le timeout;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
|
||||
|
||||
struct IocSyncptReadMaxParams {
|
||||
u32_le id;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
|
||||
|
||||
struct IocGetConfigParams {
|
||||
std::array<char, 0x41> domain_str;
|
||||
@@ -40,6 +92,12 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventSignalParams {
|
||||
u32_le user_event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
|
||||
"IocCtrlEventSignalParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventWaitParams {
|
||||
u32_le syncpt_id;
|
||||
u32_le threshold;
|
||||
@@ -48,6 +106,32 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventWaitAsyncParams {
|
||||
u32_le syncpt_id;
|
||||
u32_le threshold;
|
||||
u32_le timeout;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
|
||||
"IocCtrlEventWaitAsyncParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventRegisterParams {
|
||||
u32_le user_event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
|
||||
"IocCtrlEventRegisterParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventUnregisterParams {
|
||||
u32_le user_event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
|
||||
"IocCtrlEventUnregisterParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventKill {
|
||||
u64_le user_events;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
|
||||
|
||||
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
@@ -24,6 +24,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
|
||||
return ZCullGetCtxSize(input, output);
|
||||
case IoctlCommand::IocZcullGetInfo:
|
||||
return ZCullGetInfo(input, output);
|
||||
case IoctlCommand::IocZbcSetTable:
|
||||
return ZBCSetTable(input, output);
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -125,4 +127,13 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
IoctlZbcSetTable params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
// TODO(ogniK): What does this even actually do?
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -25,6 +25,7 @@ private:
|
||||
IocGetActiveSlotMaskCommand = 0x80084714,
|
||||
IocZcullGetCtxSizeCommand = 0x80044701,
|
||||
IocZcullGetInfo = 0x80284702,
|
||||
IocZbcSetTable = 0x402C4703,
|
||||
};
|
||||
|
||||
struct IoctlGpuCharacteristics {
|
||||
@@ -117,11 +118,21 @@ private:
|
||||
static_assert(sizeof(IoctlNvgpuGpuZcullGetInfoArgs) == 40,
|
||||
"IoctlNvgpuGpuZcullGetInfoArgs is incorrect size");
|
||||
|
||||
struct IoctlZbcSetTable {
|
||||
u32_le color_ds[4];
|
||||
u32_le color_l2[4];
|
||||
u32_le depth;
|
||||
u32_le format;
|
||||
u32_le type;
|
||||
};
|
||||
static_assert(sizeof(IoctlZbcSetTable) == 44, "IoctlZbcSetTable is incorrect size");
|
||||
|
||||
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -32,6 +32,10 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
|
||||
return AllocGPFIFOEx2(input, output);
|
||||
case IoctlCommand::IocAllocObjCtxCommand:
|
||||
return AllocateObjectContext(input, output);
|
||||
case IoctlCommand::IocChannelGetWaitbaseCommand:
|
||||
return GetWaitbase(input, output);
|
||||
case IoctlCommand::IocChannelSetTimeoutCommand:
|
||||
return ChannelSetTimeout(input, output);
|
||||
}
|
||||
|
||||
if (command.group == NVGPU_IOCTL_MAGIC) {
|
||||
@@ -138,4 +142,20 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
NGLOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlChannelSetTimeout params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout));
|
||||
NGLOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -33,6 +33,8 @@ private:
|
||||
IocChannelSetPriorityCommand = 0x4004480D,
|
||||
IocAllocGPFIFOEx2Command = 0xC020481A,
|
||||
IocAllocObjCtxCommand = 0xC0104809,
|
||||
IocChannelGetWaitbaseCommand = 0xC0080003,
|
||||
IocChannelSetTimeoutCommand = 0x40044803,
|
||||
};
|
||||
|
||||
enum class CtxObjects : u32_le {
|
||||
@@ -49,6 +51,11 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
|
||||
|
||||
struct IoctlChannelSetTimeout {
|
||||
u32_le timeout;
|
||||
};
|
||||
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
|
||||
|
||||
struct IoctlClientData {
|
||||
u64_le data;
|
||||
};
|
||||
@@ -117,7 +124,13 @@ private:
|
||||
IoctlFence fence_out; // returned new fence object for others to wait on
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence),
|
||||
"submit_gpfifo is incorrect size");
|
||||
"IoctlSubmitGpfifo is incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
|
||||
|
||||
u32_le nvmap_fd{};
|
||||
u64_le user_data{};
|
||||
@@ -133,6 +146,8 @@ private:
|
||||
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,8 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
|
||||
return IocFromId(input, output);
|
||||
case IoctlCommand::Param:
|
||||
return IocParam(input, output);
|
||||
case IoctlCommand::Free:
|
||||
return IocFree(input, output);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
@@ -45,6 +47,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
object->id = next_id++;
|
||||
object->size = params.size;
|
||||
object->status = Object::Status::Created;
|
||||
object->refcount = 1;
|
||||
|
||||
u32 handle = next_handle++;
|
||||
handles[handle] = std::move(object);
|
||||
@@ -101,6 +104,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
[&](const auto& entry) { return entry.second->id == params.id; });
|
||||
ASSERT(itr != handles.end());
|
||||
|
||||
itr->second->refcount++;
|
||||
|
||||
// Return the existing handle instead of creating a new one.
|
||||
params.handle = itr->first;
|
||||
|
||||
@@ -114,25 +119,25 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocParamParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.type);
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
|
||||
|
||||
auto object = GetObject(params.handle);
|
||||
ASSERT(object);
|
||||
ASSERT(object->status == Object::Status::Allocated);
|
||||
|
||||
switch (static_cast<ParamTypes>(params.type)) {
|
||||
switch (static_cast<ParamTypes>(params.param)) {
|
||||
case ParamTypes::Size:
|
||||
params.value = object->size;
|
||||
params.result = object->size;
|
||||
break;
|
||||
case ParamTypes::Alignment:
|
||||
params.value = object->align;
|
||||
params.result = object->align;
|
||||
break;
|
||||
case ParamTypes::Heap:
|
||||
// TODO(Subv): Seems to be a hardcoded value?
|
||||
params.value = 0x40000000;
|
||||
params.result = 0x40000000;
|
||||
break;
|
||||
case ParamTypes::Kind:
|
||||
params.value = object->kind;
|
||||
params.result = object->kind;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
@@ -142,4 +147,34 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
enum FreeFlags {
|
||||
Freed = 0,
|
||||
NotFreedYet = 1,
|
||||
};
|
||||
|
||||
IocFreeParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
|
||||
auto itr = handles.find(params.handle);
|
||||
ASSERT(itr != handles.end());
|
||||
|
||||
itr->second->refcount--;
|
||||
|
||||
params.refcount = itr->second->refcount;
|
||||
params.size = itr->second->size;
|
||||
|
||||
if (itr->second->refcount == 0)
|
||||
params.flags = Freed;
|
||||
else
|
||||
params.flags = NotFreedYet;
|
||||
|
||||
handles.erase(params.handle);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
u8 kind;
|
||||
VAddr addr;
|
||||
Status status;
|
||||
u32 refcount;
|
||||
};
|
||||
|
||||
std::shared_ptr<Object> GetObject(u32 handle) const {
|
||||
@@ -58,16 +59,25 @@ private:
|
||||
Create = 0xC0080101,
|
||||
FromId = 0xC0080103,
|
||||
Alloc = 0xC0200104,
|
||||
Free = 0xC0180105,
|
||||
Param = 0xC00C0109,
|
||||
GetId = 0xC008010E
|
||||
GetId = 0xC008010E,
|
||||
};
|
||||
|
||||
struct IocCreateParams {
|
||||
// Input
|
||||
u32_le size;
|
||||
// Output
|
||||
u32_le handle;
|
||||
};
|
||||
static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
|
||||
|
||||
struct IocFromIdParams {
|
||||
// Input
|
||||
u32_le id;
|
||||
// Output
|
||||
u32_le handle;
|
||||
};
|
||||
static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
|
||||
|
||||
struct IocAllocParams {
|
||||
// Input
|
||||
@@ -79,6 +89,25 @@ private:
|
||||
INSERT_PADDING_BYTES(7);
|
||||
u64_le addr;
|
||||
};
|
||||
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
|
||||
|
||||
struct IocFreeParams {
|
||||
u32_le handle;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u64_le refcount;
|
||||
u32_le size;
|
||||
u32_le flags;
|
||||
};
|
||||
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
|
||||
|
||||
struct IocParamParams {
|
||||
// Input
|
||||
u32_le handle;
|
||||
u32_le param;
|
||||
// Output
|
||||
u32_le result;
|
||||
};
|
||||
static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
|
||||
|
||||
struct IocGetIdParams {
|
||||
// Output
|
||||
@@ -86,27 +115,14 @@ private:
|
||||
// Input
|
||||
u32_le handle;
|
||||
};
|
||||
|
||||
struct IocFromIdParams {
|
||||
// Input
|
||||
u32_le id;
|
||||
// Output
|
||||
u32_le handle;
|
||||
};
|
||||
|
||||
struct IocParamParams {
|
||||
// Input
|
||||
u32_le handle;
|
||||
u32_le type;
|
||||
// Output
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
|
||||
|
||||
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -144,10 +144,12 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
|
||||
}
|
||||
case IPC::CommandType::ControlWithContext:
|
||||
case IPC::CommandType::Control: {
|
||||
Core::System::GetInstance().ServiceManager().InvokeControlRequest(context);
|
||||
break;
|
||||
}
|
||||
case IPC::CommandType::RequestWithContext:
|
||||
case IPC::CommandType::Request: {
|
||||
InvokeRequest(context);
|
||||
break;
|
||||
|
||||
@@ -36,7 +36,6 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
NGLOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
|
||||
ASSERT(bound_engines.find(subchannel) == bound_engines.end());
|
||||
bound_engines[subchannel] = static_cast<EngineID>(value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,6 +193,11 @@ union Instruction {
|
||||
BitField<50, 1, u64> abs_d;
|
||||
BitField<56, 1, u64> negate_imm;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred;
|
||||
BitField<42, 1, u64> negate_pred;
|
||||
} fmnmx;
|
||||
|
||||
float GetImm20_19() const {
|
||||
float result{};
|
||||
u32 imm{static_cast<u32>(imm20_19)};
|
||||
@@ -320,6 +325,7 @@ public:
|
||||
ISETP_C,
|
||||
ISETP_IMM,
|
||||
ISETP_R,
|
||||
PSETP,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
@@ -331,6 +337,7 @@ public:
|
||||
FloatSet,
|
||||
FloatSetPredicate,
|
||||
IntegerSetPredicate,
|
||||
PredicateSetPredicate,
|
||||
Conversion,
|
||||
Unknown,
|
||||
};
|
||||
@@ -477,6 +484,7 @@ private:
|
||||
INST("010010110110----", Id::ISETP_C, Type::IntegerSetPredicate, "ISETP_C"),
|
||||
INST("010110110110----", Id::ISETP_R, Type::IntegerSetPredicate, "ISETP_R"),
|
||||
INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerSetPredicate, "ISETP_IMM"),
|
||||
INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
|
||||
};
|
||||
#undef INST
|
||||
std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) {
|
||||
|
||||
@@ -58,6 +58,25 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||
ASSERT((gpu_addr & PAGE_MASK) == 0);
|
||||
|
||||
for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
|
||||
ASSERT(PageSlot(gpu_addr + offset) != static_cast<u64>(PageStatus::Allocated) &&
|
||||
PageSlot(gpu_addr + offset) != static_cast<u64>(PageStatus::Unmapped));
|
||||
PageSlot(gpu_addr + offset) = static_cast<u64>(PageStatus::Unmapped);
|
||||
}
|
||||
|
||||
// Delete the region mappings that are contained within the unmapped region
|
||||
mapped_regions.erase(std::remove_if(mapped_regions.begin(), mapped_regions.end(),
|
||||
[&](const MappedRegion& region) {
|
||||
return region.gpu_addr <= gpu_addr &&
|
||||
region.gpu_addr + region.size < gpu_addr + size;
|
||||
}),
|
||||
mapped_regions.end());
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
|
||||
GPUVAddr gpu_addr = 0;
|
||||
u64 free_space = 0;
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align);
|
||||
GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
|
||||
GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
|
||||
GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
|
||||
boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
|
||||
std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
|
||||
|
||||
|
||||
@@ -75,14 +75,11 @@ RasterizerOpenGL::RasterizerOpenGL() {
|
||||
// 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);
|
||||
// Generate VAO and UBO
|
||||
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();
|
||||
|
||||
@@ -90,7 +87,6 @@ RasterizerOpenGL::RasterizerOpenGL() {
|
||||
framebuffer.Create();
|
||||
|
||||
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);
|
||||
@@ -181,8 +177,6 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
glVertexAttribBinding(index, attrib.buffer);
|
||||
|
||||
hw_vao_enabled_attributes[index] = true;
|
||||
}
|
||||
|
||||
return {array_ptr, buffer_offset};
|
||||
|
||||
@@ -134,21 +134,17 @@ private:
|
||||
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
|
||||
OGLVertexArray sw_vao;
|
||||
OGLVertexArray hw_vao;
|
||||
std::array<bool, 16> hw_vao_enabled_attributes;
|
||||
|
||||
std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
|
||||
std::array<std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers>,
|
||||
Tegra::Engines::Maxwell3D::Regs::MaxShaderStage>
|
||||
ssbos;
|
||||
|
||||
static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> vertex_buffer;
|
||||
static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> stream_buffer;
|
||||
OGLBuffer uniform_buffer;
|
||||
OGLFramebuffer framebuffer;
|
||||
|
||||
static constexpr size_t STREAM_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> stream_buffer;
|
||||
|
||||
size_t CalculateVertexArraysSize() const;
|
||||
|
||||
std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset);
|
||||
|
||||
@@ -580,20 +580,63 @@ private:
|
||||
* @param instr Instruction to generate the if condition for.
|
||||
* @returns string containing the predicate condition.
|
||||
*/
|
||||
std::string GetPredicateCondition(Instruction instr) const {
|
||||
std::string GetPredicateCondition(u64 index, bool negate) const {
|
||||
using Tegra::Shader::Pred;
|
||||
ASSERT(instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex));
|
||||
std::string variable;
|
||||
|
||||
std::string variable =
|
||||
'p' + std::to_string(static_cast<u64>(instr.pred.pred_index.Value()));
|
||||
// Index 7 is used as an 'Always True' condition.
|
||||
if (index == static_cast<u64>(Pred::UnusedIndex))
|
||||
variable = "true";
|
||||
else
|
||||
variable = 'p' + std::to_string(index);
|
||||
|
||||
if (instr.negate_pred) {
|
||||
if (negate) {
|
||||
return "!(" + variable + ')';
|
||||
}
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comparison string to use to compare two values in the 'set' family of
|
||||
* instructions.
|
||||
* @params condition The condition used in the 'set'-family instruction.
|
||||
* @returns String corresponding to the GLSL operator that matches the desired comparison.
|
||||
*/
|
||||
std::string GetPredicateComparison(Tegra::Shader::PredCondition condition) const {
|
||||
using Tegra::Shader::PredCondition;
|
||||
static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
|
||||
{PredCondition::LessThan, "<"},
|
||||
{PredCondition::Equal, "=="},
|
||||
{PredCondition::LessEqual, "<="},
|
||||
{PredCondition::GreaterThan, ">"},
|
||||
};
|
||||
|
||||
auto comparison = PredicateComparisonStrings.find(condition);
|
||||
ASSERT_MSG(comparison != PredicateComparisonStrings.end(),
|
||||
"Unknown predicate comparison operation");
|
||||
return comparison->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operator string to use to combine two predicates in the 'setp' family of
|
||||
* instructions.
|
||||
* @params operation The operator used in the 'setp'-family instruction.
|
||||
* @returns String corresponding to the GLSL operator that matches the desired operator.
|
||||
*/
|
||||
std::string GetPredicateCombiner(Tegra::Shader::PredOperation operation) const {
|
||||
using Tegra::Shader::PredOperation;
|
||||
static const std::unordered_map<PredOperation, const char*> PredicateOperationStrings = {
|
||||
{PredOperation::And, "&&"},
|
||||
{PredOperation::Or, "||"},
|
||||
{PredOperation::Xor, "^^"},
|
||||
};
|
||||
|
||||
auto op = PredicateOperationStrings.find(operation);
|
||||
ASSERT_MSG(op != PredicateOperationStrings.end(), "Unknown predicate operation");
|
||||
return op->second;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the instruction at the specified offset is a 'sched' instruction.
|
||||
* Sched instructions always appear before a sequence of 3 instructions.
|
||||
@@ -634,7 +677,9 @@ private:
|
||||
"NeverExecute predicate not implemented");
|
||||
|
||||
if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
shader.AddLine("if (" + GetPredicateCondition(instr) + ')');
|
||||
shader.AddLine("if (" +
|
||||
GetPredicateCondition(instr.pred.pred_index, instr.negate_pred != 0) +
|
||||
')');
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
}
|
||||
@@ -730,6 +775,16 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMNMX: {
|
||||
std::string condition =
|
||||
GetPredicateCondition(instr.alu.fmnmx.pred, instr.alu.fmnmx.negate_pred != 0);
|
||||
std::string parameters = op_a + ',' + op_b;
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
'(' + condition + ") ? min(" + parameters + ") : max(" +
|
||||
parameters + ')',
|
||||
1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::RRO: {
|
||||
NGLOG_DEBUG(HW_GPU, "Skipping RRO instruction");
|
||||
break;
|
||||
@@ -873,28 +928,25 @@ private:
|
||||
}
|
||||
|
||||
using Tegra::Shader::Pred;
|
||||
ASSERT_MSG(instr.fsetp.pred0 == static_cast<u64>(Pred::UnusedIndex) &&
|
||||
instr.fsetp.pred39 == static_cast<u64>(Pred::UnusedIndex),
|
||||
"Compound predicates are not implemented");
|
||||
|
||||
// We can't use the constant predicate as destination.
|
||||
ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
|
||||
|
||||
using Tegra::Shader::PredCondition;
|
||||
switch (instr.fsetp.cond) {
|
||||
case PredCondition::LessThan:
|
||||
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") < (" + op_b + ')');
|
||||
break;
|
||||
case PredCondition::Equal:
|
||||
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") == (" + op_b + ')');
|
||||
break;
|
||||
case PredCondition::LessEqual:
|
||||
SetPredicate(instr.fsetp.pred3, '(' + op_a + ") <= (" + op_b + ')');
|
||||
break;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})",
|
||||
static_cast<unsigned>(instr.fsetp.cond.Value()), op_a, op_b);
|
||||
UNREACHABLE();
|
||||
std::string second_pred =
|
||||
GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0);
|
||||
|
||||
std::string comparator = GetPredicateComparison(instr.fsetp.cond);
|
||||
std::string combiner = GetPredicateCombiner(instr.fsetp.op);
|
||||
|
||||
std::string predicate = '(' + op_a + ") " + comparator + " (" + op_b + ')';
|
||||
// Set the primary predicate to the result of Predicate OP SecondPredicate
|
||||
SetPredicate(instr.fsetp.pred3,
|
||||
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
|
||||
|
||||
if (instr.fsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
// Set the secondary predicate to the result of !Predicate OP SecondPredicate, if
|
||||
// enabled
|
||||
SetPredicate(instr.fsetp.pred0,
|
||||
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -926,35 +978,18 @@ private:
|
||||
op_b = "abs(" + op_b + ')';
|
||||
}
|
||||
|
||||
using Tegra::Shader::Pred;
|
||||
ASSERT_MSG(instr.fset.pred39 == static_cast<u64>(Pred::UnusedIndex),
|
||||
"Compound predicates are not implemented");
|
||||
|
||||
// The fset instruction sets a register to 1.0 if the condition is true, and to 0
|
||||
// otherwise.
|
||||
using Tegra::Shader::PredCondition;
|
||||
switch (instr.fset.cond) {
|
||||
case PredCondition::LessThan:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") < (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
break;
|
||||
case PredCondition::Equal:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") == (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
break;
|
||||
case PredCondition::LessEqual:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") <= (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
break;
|
||||
case PredCondition::GreaterThan:
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0,
|
||||
"((" + op_a + ") > (" + op_b + ")) ? 1.0 : 0", 1, 1);
|
||||
break;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})",
|
||||
static_cast<unsigned>(instr.fset.cond.Value()), op_a, op_b);
|
||||
UNREACHABLE();
|
||||
}
|
||||
std::string second_pred =
|
||||
GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0);
|
||||
|
||||
std::string comparator = GetPredicateComparison(instr.fset.cond);
|
||||
std::string combiner = GetPredicateCombiner(instr.fset.op);
|
||||
|
||||
std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " +
|
||||
combiner + " (" + second_pred + "))";
|
||||
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
@@ -11,6 +12,27 @@
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
/**
|
||||
* Utility function to log the source code of a list of shaders.
|
||||
* @param shaders The OpenGL shaders whose source we will print.
|
||||
*/
|
||||
template <typename... T>
|
||||
void LogShaderSource(T... shaders) {
|
||||
auto shader_list = {shaders...};
|
||||
|
||||
for (const auto& shader : shader_list) {
|
||||
if (shader == 0)
|
||||
continue;
|
||||
|
||||
GLint source_length;
|
||||
glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length);
|
||||
|
||||
std::string source(source_length, ' ');
|
||||
glGetShaderSource(shader, source_length, nullptr, &source[0]);
|
||||
NGLOG_INFO(Render_OpenGL, "Shader source {}", source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to create and compile an OpenGL GLSL shader
|
||||
* @param source String of the GLSL shader program
|
||||
@@ -55,6 +77,11 @@ GLuint LoadProgram(bool separable_program, T... shaders) {
|
||||
}
|
||||
}
|
||||
|
||||
if (result == GL_FALSE) {
|
||||
// There was a problem linking the shader, print the source for debugging purposes.
|
||||
LogShaderSource(shaders...);
|
||||
}
|
||||
|
||||
ASSERT_MSG(result == GL_TRUE, "Shader not linked");
|
||||
|
||||
((shaders == 0 ? (void)0 : glDetachShader(program_id, shaders)), ...);
|
||||
|
||||
@@ -98,6 +98,30 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() cons
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeCallstack::WaitTreeCallstack(const Kernel::Thread& thread) : thread(thread) {}
|
||||
|
||||
QString WaitTreeCallstack::GetText() const {
|
||||
return tr("Call stack");
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list;
|
||||
|
||||
constexpr size_t BaseRegister = 29;
|
||||
u64 base_pointer = thread.context.cpu_registers[BaseRegister];
|
||||
|
||||
while (base_pointer != 0) {
|
||||
u64 lr = Memory::Read64(base_pointer + sizeof(u64));
|
||||
if (lr == 0)
|
||||
break;
|
||||
list.push_back(
|
||||
std::make_unique<WaitTreeText>(tr("0x%1").arg(lr - sizeof(u32), 16, 16, QChar('0'))));
|
||||
base_pointer = Memory::Read64(base_pointer);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {}
|
||||
|
||||
bool WaitTreeExpandableItem::IsExpandable() const {
|
||||
@@ -269,6 +293,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
thread.IsSleepingOnWaitAll()));
|
||||
}
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeCallstack>(thread));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,17 @@ private:
|
||||
Kernel::SharedPtr<Kernel::Thread> owner;
|
||||
};
|
||||
|
||||
class WaitTreeCallstack : public WaitTreeExpandableItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeCallstack(const Kernel::Thread& thread);
|
||||
QString GetText() const override;
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
|
||||
private:
|
||||
const Kernel::Thread& thread;
|
||||
};
|
||||
|
||||
class WaitTreeWaitObject : public WaitTreeExpandableItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -7,8 +7,19 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
#include "yuzu_cmd/config.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <getopt.h>
|
||||
@@ -24,19 +35,6 @@
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
#include "yuzu_cmd/config.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
||||
|
||||
Reference in New Issue
Block a user