Some small sync primitive fixes, logging fixes, started to implement the 2D engine on the GPU, fixed DrawArrays, implemented a few more shader instructions, made a start on nvdrv refactor, etc...
This commit is contained in:
parent
211f7f69db
commit
a38a72b062
27 changed files with 816 additions and 199 deletions
|
@ -14,23 +14,23 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
public KThread Thread { get; private set; }
|
public KThread Thread { get; private set; }
|
||||||
|
|
||||||
public ManualResetEvent SyncWaitEvent { get; private set; }
|
public bool IsActive { get; set; }
|
||||||
public AutoResetEvent SchedWaitEvent { get; private set; }
|
|
||||||
|
|
||||||
public bool Active { get; set; }
|
public AutoResetEvent WaitSync { get; private set; }
|
||||||
|
public ManualResetEvent WaitActivity { get; private set; }
|
||||||
public int SyncTimeout { get; set; }
|
public AutoResetEvent WaitSched { get; private set; }
|
||||||
|
|
||||||
public SchedulerThread(KThread Thread)
|
public SchedulerThread(KThread Thread)
|
||||||
{
|
{
|
||||||
this.Thread = Thread;
|
this.Thread = Thread;
|
||||||
|
|
||||||
SyncWaitEvent = new ManualResetEvent(true);
|
IsActive = true;
|
||||||
SchedWaitEvent = new AutoResetEvent(false);
|
|
||||||
|
|
||||||
Active = true;
|
WaitSync = new AutoResetEvent(false);
|
||||||
|
|
||||||
SyncTimeout = 0;
|
WaitActivity = new ManualResetEvent(true);
|
||||||
|
|
||||||
|
WaitSched = new AutoResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -42,8 +42,11 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
if (Disposing)
|
if (Disposing)
|
||||||
{
|
{
|
||||||
SyncWaitEvent.Dispose();
|
WaitSync.Dispose();
|
||||||
SchedWaitEvent.Dispose();
|
|
||||||
|
WaitActivity.Dispose();
|
||||||
|
|
||||||
|
WaitSched.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,25 +209,46 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedThread.Active = Active;
|
SchedThread.IsActive = Active;
|
||||||
|
|
||||||
UpdateSyncWaitEvent(SchedThread);
|
if (Active)
|
||||||
|
{
|
||||||
WaitIfNeeded(SchedThread);
|
SchedThread.WaitActivity.Set();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SchedThread.WaitActivity.Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnterWait(KThread Thread, int Timeout = -1)
|
public void EnterWait(KThread Thread)
|
||||||
{
|
{
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedThread.SyncTimeout = Timeout;
|
Suspend(Thread.ProcessorId);
|
||||||
|
|
||||||
UpdateSyncWaitEvent(SchedThread);
|
SchedThread.WaitSync.WaitOne();
|
||||||
|
|
||||||
return WaitIfNeeded(SchedThread);
|
TryResumingExecution(SchedThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnterWait(KThread Thread, int Timeout)
|
||||||
|
{
|
||||||
|
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Suspend(Thread.ProcessorId);
|
||||||
|
|
||||||
|
bool Result = SchedThread.WaitSync.WaitOne(Timeout);
|
||||||
|
|
||||||
|
TryResumingExecution(SchedThread);
|
||||||
|
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WakeUp(KThread Thread)
|
public void WakeUp(KThread Thread)
|
||||||
|
@ -234,39 +258,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedThread.SyncTimeout = 0;
|
SchedThread.WaitSync.Set();
|
||||||
|
|
||||||
UpdateSyncWaitEvent(SchedThread);
|
|
||||||
|
|
||||||
WaitIfNeeded(SchedThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSyncWaitEvent(SchedulerThread SchedThread)
|
|
||||||
{
|
|
||||||
if (SchedThread.Active && SchedThread.SyncTimeout == 0)
|
|
||||||
{
|
|
||||||
SchedThread.SyncWaitEvent.Set();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SchedThread.SyncWaitEvent.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool WaitIfNeeded(SchedulerThread SchedThread)
|
|
||||||
{
|
|
||||||
KThread Thread = SchedThread.Thread;
|
|
||||||
|
|
||||||
if (!IsActive(SchedThread) && Thread.Thread.IsCurrentThread())
|
|
||||||
{
|
|
||||||
Suspend(Thread.ProcessorId);
|
|
||||||
|
|
||||||
return Resume(Thread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Suspend(int ProcessorId)
|
public void Suspend(int ProcessorId)
|
||||||
|
@ -292,53 +284,52 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "yielded execution.");
|
PrintDbgThreadInfo(Thread, "yielded execution.");
|
||||||
|
|
||||||
lock (SchedLock)
|
if (IsActive(Thread))
|
||||||
{
|
{
|
||||||
SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority);
|
lock (SchedLock)
|
||||||
|
|
||||||
if (IsActive(Thread) && SchedThread == null)
|
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
|
SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority);
|
||||||
|
|
||||||
return;
|
if (SchedThread == null)
|
||||||
}
|
{
|
||||||
|
PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
|
||||||
|
|
||||||
if (SchedThread != null)
|
return;
|
||||||
{
|
}
|
||||||
RunThread(SchedThread);
|
|
||||||
|
if (SchedThread != null)
|
||||||
|
{
|
||||||
|
RunThread(SchedThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Just stop running the thread if it's not active,
|
||||||
|
//and run whatever is waiting to run with the higuest priority.
|
||||||
|
Suspend(Thread.ProcessorId);
|
||||||
|
}
|
||||||
|
|
||||||
Resume(Thread);
|
Resume(Thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Resume(KThread Thread)
|
public void Resume(KThread Thread)
|
||||||
{
|
{
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryResumingExecution(SchedThread);
|
TryResumingExecution(SchedThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryResumingExecution(SchedulerThread SchedThread)
|
private void TryResumingExecution(SchedulerThread SchedThread)
|
||||||
{
|
{
|
||||||
KThread Thread = SchedThread.Thread;
|
KThread Thread = SchedThread.Thread;
|
||||||
|
|
||||||
if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
|
PrintDbgThreadInfo(Thread, "trying to resume...");
|
||||||
{
|
|
||||||
PrintDbgThreadInfo(Thread, "entering inactive wait state...");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Result = false;
|
SchedThread.WaitActivity.WaitOne();
|
||||||
|
|
||||||
if (SchedThread.SyncTimeout != 0)
|
|
||||||
{
|
|
||||||
Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout);
|
|
||||||
|
|
||||||
SchedThread.SyncTimeout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
|
@ -346,7 +337,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
PrintDbgThreadInfo(Thread, "resuming execution...");
|
PrintDbgThreadInfo(Thread, "resuming execution...");
|
||||||
|
|
||||||
return Result;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
||||||
|
@ -354,18 +345,16 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
PrintDbgThreadInfo(Thread, "entering wait state...");
|
PrintDbgThreadInfo(Thread, "entering wait state...");
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedThread.SchedWaitEvent.WaitOne();
|
SchedThread.WaitSched.WaitOne();
|
||||||
|
|
||||||
PrintDbgThreadInfo(Thread, "resuming execution...");
|
PrintDbgThreadInfo(Thread, "resuming execution...");
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunThread(SchedulerThread SchedThread)
|
private void RunThread(SchedulerThread SchedThread)
|
||||||
{
|
{
|
||||||
if (!SchedThread.Thread.Thread.Execute())
|
if (!SchedThread.Thread.Thread.Execute())
|
||||||
{
|
{
|
||||||
SchedThread.SchedWaitEvent.Set();
|
SchedThread.WaitSched.Set();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -380,21 +369,16 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return IsActive(SchedThread);
|
return SchedThread.IsActive;
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsActive(SchedulerThread SchedThread)
|
|
||||||
{
|
|
||||||
return SchedThread.Active && SchedThread.SyncTimeout == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrintDbgThreadInfo(KThread Thread, string Message)
|
private void PrintDbgThreadInfo(KThread Thread, string Message)
|
||||||
{
|
{
|
||||||
Log.PrintDebug(LogClass.KernelScheduler, "(" +
|
Log.PrintDebug(LogClass.KernelScheduler, "(" +
|
||||||
"ThreadId: " + Thread.ThreadId + ", " +
|
"ThreadId = " + Thread.ThreadId + ", " +
|
||||||
"ProcessorId: " + Thread.ProcessorId + ", " +
|
"ProcessorId = " + Thread.ProcessorId + ", " +
|
||||||
"ActualPriority: " + Thread.ActualPriority + ", " +
|
"ActualPriority = " + Thread.ActualPriority + ", " +
|
||||||
"WantedPriority: " + Thread.WantedPriority + ") " + Message);
|
"WantedPriority = " + Thread.WantedPriority + ") " + Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -198,5 +198,35 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetBufferType0x21Position()
|
||||||
|
{
|
||||||
|
if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0)
|
||||||
|
{
|
||||||
|
return PtrBuff[0].Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SendBuff.Count > 0)
|
||||||
|
{
|
||||||
|
return SendBuff[0].Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetBufferType0x22Position()
|
||||||
|
{
|
||||||
|
if (RecvListBuff.Count > 0 && RecvListBuff[0].Position != 0)
|
||||||
|
{
|
||||||
|
return RecvListBuff[0].Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReceiveBuff.Count > 0)
|
||||||
|
{
|
||||||
|
return ReceiveBuff[0].Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
||||||
{
|
{
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
|
||||||
|
|
||||||
Func(ThreadState);
|
Func(ThreadState);
|
||||||
|
|
||||||
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
|
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
|
||||||
|
|
|
@ -18,6 +18,11 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
long MutexAddress = (long)ThreadState.X1;
|
long MutexAddress = (long)ThreadState.X1;
|
||||||
int WaitThreadHandle = (int)ThreadState.X2;
|
int WaitThreadHandle = (int)ThreadState.X2;
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||||
|
"OwnerThreadHandle = " + OwnerThreadHandle.ToString("x8") + ", " +
|
||||||
|
"MutexAddress = " + MutexAddress .ToString("x16") + ", " +
|
||||||
|
"WaitThreadHandle = " + WaitThreadHandle .ToString("x8"));
|
||||||
|
|
||||||
if (IsPointingInsideKernel(MutexAddress))
|
if (IsPointingInsideKernel(MutexAddress))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||||
|
@ -38,6 +43,8 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
|
KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "lock tid: " + OwnerThread.ThreadId.ToString());
|
||||||
|
|
||||||
if (OwnerThread == null)
|
if (OwnerThread == null)
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!");
|
||||||
|
@ -69,6 +76,8 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
{
|
{
|
||||||
long MutexAddress = (long)ThreadState.X0;
|
long MutexAddress = (long)ThreadState.X0;
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = " + MutexAddress.ToString("x16"));
|
||||||
|
|
||||||
if (IsPointingInsideKernel(MutexAddress))
|
if (IsPointingInsideKernel(MutexAddress))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||||
|
@ -102,6 +111,12 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
int ThreadHandle = (int)ThreadState.X2;
|
int ThreadHandle = (int)ThreadState.X2;
|
||||||
ulong Timeout = ThreadState.X3;
|
ulong Timeout = ThreadState.X3;
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||||
|
"OwnerThreadHandle = " + MutexAddress .ToString("x16") + ", " +
|
||||||
|
"MutexAddress = " + CondVarAddress.ToString("x16") + ", " +
|
||||||
|
"WaitThreadHandle = " + ThreadHandle .ToString("x8") + ", " +
|
||||||
|
"Timeout = " + Timeout .ToString("x16"));
|
||||||
|
|
||||||
if (IsPointingInsideKernel(MutexAddress))
|
if (IsPointingInsideKernel(MutexAddress))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||||
|
@ -166,6 +181,8 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
{
|
{
|
||||||
int MutexValue = Process.Memory.ReadInt32(MutexAddress);
|
int MutexValue = Process.Memory.ReadInt32(MutexAddress);
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
|
||||||
|
|
||||||
if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
|
if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -176,7 +193,7 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
InsertWaitingMutexThread(OwnerThreadHandle, WaitThread);
|
InsertWaitingMutexThread(OwnerThreadHandle, WaitThread);
|
||||||
|
|
||||||
Process.Scheduler.EnterWait(WaitThread);
|
Process.Scheduler.EnterWait(CurrThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MutexUnlock(KThread CurrThread, long MutexAddress)
|
private bool MutexUnlock(KThread CurrThread, long MutexAddress)
|
||||||
|
@ -199,8 +216,12 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
OwnerThread = OwnerThread.NextMutexThread;
|
OwnerThread = OwnerThread.NextMutexThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateMutexOwner(CurrThread, OwnerThread, MutexAddress);
|
||||||
|
|
||||||
CurrThread.NextMutexThread = null;
|
CurrThread.NextMutexThread = null;
|
||||||
|
|
||||||
|
CurrThread.UpdatePriority();
|
||||||
|
|
||||||
if (OwnerThread != null)
|
if (OwnerThread != null)
|
||||||
{
|
{
|
||||||
int HasListeners = OwnerThread.NextMutexThread != null ? MutexHasListenersMask : 0;
|
int HasListeners = OwnerThread.NextMutexThread != null ? MutexHasListenersMask : 0;
|
||||||
|
@ -284,7 +305,9 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Process.Scheduler.EnterWait(WaitThread);
|
Process.Scheduler.EnterWait(WaitThread);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,8 +337,6 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
|
|
||||||
int MutexValue = Process.Memory.ReadInt32(CurrThread.MutexAddress);
|
int MutexValue = Process.Memory.ReadInt32(CurrThread.MutexAddress);
|
||||||
|
|
||||||
MutexValue &= ~MutexHasListenersMask;
|
|
||||||
|
|
||||||
if (MutexValue == 0)
|
if (MutexValue == 0)
|
||||||
{
|
{
|
||||||
//Give the lock to this thread.
|
//Give the lock to this thread.
|
||||||
|
@ -325,15 +346,17 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
CurrThread.MutexAddress = 0;
|
CurrThread.MutexAddress = 0;
|
||||||
CurrThread.CondVarAddress = 0;
|
CurrThread.CondVarAddress = 0;
|
||||||
|
|
||||||
CurrThread.MutexOwner = null;
|
CurrThread.MutexOwner?.UpdatePriority();
|
||||||
|
|
||||||
CurrThread.UpdatePriority();
|
CurrThread.MutexOwner = null;
|
||||||
|
|
||||||
Process.Scheduler.WakeUp(CurrThread);
|
Process.Scheduler.WakeUp(CurrThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Wait until the lock is released.
|
//Wait until the lock is released.
|
||||||
|
MutexValue &= ~MutexHasListenersMask;
|
||||||
|
|
||||||
InsertWaitingMutexThread(MutexValue, CurrThread);
|
InsertWaitingMutexThread(MutexValue, CurrThread);
|
||||||
|
|
||||||
MutexValue |= MutexHasListenersMask;
|
MutexValue |= MutexHasListenersMask;
|
||||||
|
@ -399,6 +422,23 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
OwnerThread.UpdatePriority();
|
OwnerThread.UpdatePriority();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateMutexOwner(KThread CurrThread, KThread NewOwner, long MutexAddress)
|
||||||
|
{
|
||||||
|
//Go through all threads waiting for the mutex,
|
||||||
|
//and update the MutexOwner field to point to the new owner.
|
||||||
|
CurrThread = CurrThread.NextMutexThread;
|
||||||
|
|
||||||
|
while (CurrThread != null)
|
||||||
|
{
|
||||||
|
if (CurrThread.MutexAddress == MutexAddress)
|
||||||
|
{
|
||||||
|
CurrThread.MutexOwner = NewOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrThread = CurrThread.NextMutexThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void AcquireMutexValue(long MutexAddress)
|
private void AcquireMutexValue(long MutexAddress)
|
||||||
{
|
{
|
||||||
while (!Process.Memory.AcquireAddress(MutexAddress))
|
while (!Process.Memory.AcquireAddress(MutexAddress))
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
{
|
{
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
|
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
|
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
|
||||||
|
{ ("/dev/nvhost-as-gpu", 0x4105), NvGpuAsIoctlUnmap },
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
|
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
|
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
|
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
|
||||||
|
@ -201,6 +202,19 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long NvGpuAsIoctlUnmap(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
long Offset = Reader.ReadInt64();
|
||||||
|
|
||||||
|
Context.Ns.Gpu.MemoryMgr.Unmap(Offset, 0x10000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
|
private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.GetSendBuffPtr();
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
@ -319,6 +333,19 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
int Padding = Reader.ReadInt32();
|
int Padding = Reader.ReadInt32();
|
||||||
int Offset = Reader.ReadInt32();
|
int Offset = Reader.ReadInt32();
|
||||||
int Pages = Reader.ReadInt32();
|
int Pages = Reader.ReadInt32();
|
||||||
|
|
||||||
|
NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
|
||||||
|
|
||||||
|
if (Map == null)
|
||||||
|
{
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!");
|
||||||
|
|
||||||
|
return -1; //TODO: Corrent error code.
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.Ns.Gpu.MapMemory(Map.CpuAddress,
|
||||||
|
(long)(uint)Offset << 16,
|
||||||
|
(long)(uint)Pages << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
|
|
31
Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs
Normal file
31
Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
|
{
|
||||||
|
class NvChNvMap
|
||||||
|
{
|
||||||
|
private static ConcurrentDictionary<Process, IdDictionary> NvMaps;
|
||||||
|
|
||||||
|
public void Create(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||||
|
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||||
|
|
||||||
|
int Size = Context.Memory.ReadInt32(InputPosition);
|
||||||
|
|
||||||
|
int Handle = AddNvMap(Context, new NvMap(Size));
|
||||||
|
|
||||||
|
Context.Memory.WriteInt32(OutputPosition, Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AddNvMap(ServiceCtx Context, NvMap Map)
|
||||||
|
{
|
||||||
|
return NvMaps[Context.Process].Add(Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NvMap GetNvMap(ServiceCtx Context, int Handle)
|
||||||
|
{
|
||||||
|
return NvMaps[Context.Process].GetData<NvMap>(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,5 +9,12 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
public int Kind;
|
public int Kind;
|
||||||
public long CpuAddress;
|
public long CpuAddress;
|
||||||
public long GpuAddress;
|
public long GpuAddress;
|
||||||
|
|
||||||
|
public NvMap() { }
|
||||||
|
|
||||||
|
public NvMap(int Size)
|
||||||
|
{
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Gal
|
||||||
|
|
||||||
void SetViewport(int X, int Y, int Width, int Height);
|
void SetViewport(int X, int Y, int Width, int Height);
|
||||||
|
|
||||||
|
void GetFrameBufferData(long Tag, Action<byte[]> Callback);
|
||||||
|
|
||||||
//Rasterizer
|
//Rasterizer
|
||||||
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
|
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
|
|
||||||
void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
|
void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
|
||||||
|
|
||||||
void DrawArrays(int VbIndex, GalPrimitiveType PrimType);
|
void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType);
|
||||||
|
|
||||||
void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
|
void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
|
||||||
|
|
||||||
|
|
|
@ -270,6 +270,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GetBufferData(long Tag, Action<byte[]> Callback)
|
||||||
|
{
|
||||||
|
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
|
||||||
|
{
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle);
|
||||||
|
|
||||||
|
byte[] Data = new byte[Fb.Width * Fb.Height * 4];
|
||||||
|
|
||||||
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
|
||||||
|
|
||||||
|
GL.ReadPixels(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Fb.Width,
|
||||||
|
Fb.Height,
|
||||||
|
Format,
|
||||||
|
Type,
|
||||||
|
Data);
|
||||||
|
|
||||||
|
Callback(Data);
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrFbHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetViewport(Rect Viewport)
|
private void SetViewport(Rect Viewport)
|
||||||
{
|
{
|
||||||
GL.Viewport(
|
GL.Viewport(
|
||||||
|
|
|
@ -48,8 +48,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
public int VaoHandle;
|
public int VaoHandle;
|
||||||
public int VboHandle;
|
public int VboHandle;
|
||||||
|
|
||||||
public int PrimCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct IbInfo
|
private struct IbInfo
|
||||||
|
@ -102,8 +100,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
EnsureVbInitialized(VbIndex);
|
EnsureVbInitialized(VbIndex);
|
||||||
|
|
||||||
VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride;
|
|
||||||
|
|
||||||
VbInfo Vb = VertexBuffers[VbIndex];
|
VbInfo Vb = VertexBuffers[VbIndex];
|
||||||
|
|
||||||
IntPtr Length = new IntPtr(Buffer.Length);
|
IntPtr Length = new IntPtr(Buffer.Length);
|
||||||
|
@ -171,29 +167,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
|
public void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType)
|
||||||
{
|
{
|
||||||
VbInfo Vb = VertexBuffers[VbIndex];
|
if (PrimCount == 0)
|
||||||
|
|
||||||
if (Vb.PrimCount == 0)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VbInfo Vb = VertexBuffers[VbIndex];
|
||||||
|
|
||||||
GL.BindVertexArray(Vb.VaoHandle);
|
GL.BindVertexArray(Vb.VaoHandle);
|
||||||
|
|
||||||
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), 0, Vb.PrimCount);
|
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
|
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
|
||||||
{
|
{
|
||||||
VbInfo Vb = VertexBuffers[VbIndex];
|
VbInfo Vb = VertexBuffers[VbIndex];
|
||||||
|
|
||||||
if (Vb.PrimCount == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
|
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
|
||||||
|
|
||||||
GL.BindVertexArray(Vb.VaoHandle);
|
GL.BindVertexArray(Vb.VaoHandle);
|
||||||
|
|
|
@ -146,6 +146,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height));
|
ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GetFrameBufferData(long Tag, Action<byte[]> Callback)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => FrameBuffer.GetBufferData(Tag, Callback));
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
|
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
|
ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
|
||||||
|
@ -173,14 +178,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format));
|
ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
|
public void DrawArrays(int VbIndex, int First, int PrimCount, GalPrimitiveType PrimType)
|
||||||
{
|
{
|
||||||
if ((uint)VbIndex > 31)
|
if ((uint)VbIndex > 31)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(VbIndex));
|
throw new ArgumentOutOfRangeException(nameof(VbIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, PrimType));
|
ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, First, PrimCount, PrimType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
|
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
|
||||||
|
|
|
@ -31,51 +31,53 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
|
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
|
||||||
{
|
{
|
||||||
{ ShaderIrInst.And, GetAndExpr },
|
{ ShaderIrInst.And, GetAndExpr },
|
||||||
{ ShaderIrInst.Asr, GetAsrExpr },
|
{ ShaderIrInst.Asr, GetAsrExpr },
|
||||||
{ ShaderIrInst.Band, GetBandExpr },
|
{ ShaderIrInst.Band, GetBandExpr },
|
||||||
{ ShaderIrInst.Bnot, GetBnotExpr },
|
{ ShaderIrInst.Bnot, GetBnotExpr },
|
||||||
{ ShaderIrInst.Ceil, GetCeilExpr },
|
{ ShaderIrInst.Ceil, GetCeilExpr },
|
||||||
{ ShaderIrInst.Ceq, GetCeqExpr },
|
{ ShaderIrInst.Ceq, GetCeqExpr },
|
||||||
{ ShaderIrInst.Cge, GetCgeExpr },
|
{ ShaderIrInst.Cge, GetCgeExpr },
|
||||||
{ ShaderIrInst.Cgt, GetCgtExpr },
|
{ ShaderIrInst.Cgt, GetCgtExpr },
|
||||||
{ ShaderIrInst.Clamp, GetClampExpr },
|
{ ShaderIrInst.Clamps, GetClampsExpr },
|
||||||
{ ShaderIrInst.Cle, GetCleExpr },
|
{ ShaderIrInst.Clampu, GetClampuExpr },
|
||||||
{ ShaderIrInst.Clt, GetCltExpr },
|
{ ShaderIrInst.Cle, GetCleExpr },
|
||||||
{ ShaderIrInst.Cne, GetCneExpr },
|
{ ShaderIrInst.Clt, GetCltExpr },
|
||||||
{ ShaderIrInst.Exit, GetExitExpr },
|
{ ShaderIrInst.Cne, GetCneExpr },
|
||||||
{ ShaderIrInst.Fabs, GetFabsExpr },
|
{ ShaderIrInst.Exit, GetExitExpr },
|
||||||
{ ShaderIrInst.Fadd, GetFaddExpr },
|
{ ShaderIrInst.Fabs, GetFabsExpr },
|
||||||
{ ShaderIrInst.Fceq, GetCeqExpr },
|
{ ShaderIrInst.Fadd, GetFaddExpr },
|
||||||
{ ShaderIrInst.Fcge, GetCgeExpr },
|
{ ShaderIrInst.Fceq, GetCeqExpr },
|
||||||
{ ShaderIrInst.Fcgt, GetCgtExpr },
|
{ ShaderIrInst.Fcge, GetCgeExpr },
|
||||||
{ ShaderIrInst.Fcle, GetCleExpr },
|
{ ShaderIrInst.Fcgt, GetCgtExpr },
|
||||||
{ ShaderIrInst.Fclt, GetCltExpr },
|
{ ShaderIrInst.Fclamp, GetFclampExpr },
|
||||||
{ ShaderIrInst.Fcne, GetCneExpr },
|
{ ShaderIrInst.Fcle, GetCleExpr },
|
||||||
{ ShaderIrInst.Fcos, GetFcosExpr },
|
{ ShaderIrInst.Fclt, GetCltExpr },
|
||||||
{ ShaderIrInst.Fex2, GetFex2Expr },
|
{ ShaderIrInst.Fcne, GetCneExpr },
|
||||||
{ ShaderIrInst.Ffma, GetFfmaExpr },
|
{ ShaderIrInst.Fcos, GetFcosExpr },
|
||||||
{ ShaderIrInst.Flg2, GetFlg2Expr },
|
{ ShaderIrInst.Fex2, GetFex2Expr },
|
||||||
{ ShaderIrInst.Floor, GetFloorExpr },
|
{ ShaderIrInst.Ffma, GetFfmaExpr },
|
||||||
{ ShaderIrInst.Fmul, GetFmulExpr },
|
{ ShaderIrInst.Flg2, GetFlg2Expr },
|
||||||
{ ShaderIrInst.Fneg, GetFnegExpr },
|
{ ShaderIrInst.Floor, GetFloorExpr },
|
||||||
{ ShaderIrInst.Frcp, GetFrcpExpr },
|
{ ShaderIrInst.Fmul, GetFmulExpr },
|
||||||
{ ShaderIrInst.Frsq, GetFrsqExpr },
|
{ ShaderIrInst.Fneg, GetFnegExpr },
|
||||||
{ ShaderIrInst.Fsin, GetFsinExpr },
|
{ ShaderIrInst.Frcp, GetFrcpExpr },
|
||||||
{ ShaderIrInst.Ftos, GetFtosExpr },
|
{ ShaderIrInst.Frsq, GetFrsqExpr },
|
||||||
{ ShaderIrInst.Ftou, GetFtouExpr },
|
{ ShaderIrInst.Fsin, GetFsinExpr },
|
||||||
{ ShaderIrInst.Ipa, GetIpaExpr },
|
{ ShaderIrInst.Ftos, GetFtosExpr },
|
||||||
{ ShaderIrInst.Kil, GetKilExpr },
|
{ ShaderIrInst.Ftou, GetFtouExpr },
|
||||||
{ ShaderIrInst.Lsr, GetLsrExpr },
|
{ ShaderIrInst.Ipa, GetIpaExpr },
|
||||||
{ ShaderIrInst.Not, GetNotExpr },
|
{ ShaderIrInst.Kil, GetKilExpr },
|
||||||
{ ShaderIrInst.Or, GetOrExpr },
|
{ ShaderIrInst.Lsr, GetLsrExpr },
|
||||||
{ ShaderIrInst.Stof, GetStofExpr },
|
{ ShaderIrInst.Not, GetNotExpr },
|
||||||
{ ShaderIrInst.Texq, GetTexqExpr },
|
{ ShaderIrInst.Or, GetOrExpr },
|
||||||
{ ShaderIrInst.Texs, GetTexsExpr },
|
{ ShaderIrInst.Stof, GetStofExpr },
|
||||||
{ ShaderIrInst.Trunc, GetTruncExpr },
|
{ ShaderIrInst.Texq, GetTexqExpr },
|
||||||
{ ShaderIrInst.Txlf, GetTxlfExpr },
|
{ ShaderIrInst.Texs, GetTexsExpr },
|
||||||
{ ShaderIrInst.Utof, GetUtofExpr },
|
{ ShaderIrInst.Trunc, GetTruncExpr },
|
||||||
{ ShaderIrInst.Xor, GetXorExpr }
|
{ ShaderIrInst.Txlf, GetTxlfExpr },
|
||||||
|
{ ShaderIrInst.Utof, GetUtofExpr },
|
||||||
|
{ ShaderIrInst.Xor, GetXorExpr }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,7 +480,19 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil");
|
private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil");
|
||||||
|
|
||||||
private string GetClampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp");
|
private string GetClampsExpr(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
return "clamp(" + GetOperExpr(Op, Op.OperandA) + ", " +
|
||||||
|
GetOperExpr(Op, Op.OperandB) + ", " +
|
||||||
|
GetOperExpr(Op, Op.OperandC) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetClampuExpr(ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
return "int(clamp(uint(" + GetOperExpr(Op, Op.OperandA) + "), " +
|
||||||
|
"uint(" + GetOperExpr(Op, Op.OperandB) + "), " +
|
||||||
|
"uint(" + GetOperExpr(Op, Op.OperandC) + ")))";
|
||||||
|
}
|
||||||
|
|
||||||
private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
|
private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
|
||||||
private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
|
private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
|
||||||
|
@ -499,6 +513,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+");
|
private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+");
|
||||||
|
|
||||||
|
private string GetFclampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp");
|
||||||
|
|
||||||
private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2");
|
private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2");
|
||||||
|
|
||||||
private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
|
private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
|
||||||
|
|
|
@ -66,6 +66,21 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul);
|
EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Fset_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitFset(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fset_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitFset(Block, OpCode, ShaderOper.Immf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fset_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitFset(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
|
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
|
||||||
{
|
{
|
||||||
EmitFsetp(Block, OpCode, ShaderOper.CR);
|
EmitFsetp(Block, OpCode, ShaderOper.CR);
|
||||||
|
@ -279,6 +294,78 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
EmitSet(Block, OpCode, true, Oper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitIset(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
EmitSet(Block, OpCode, false, Oper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
bool Na = ((OpCode >> 43) & 1) != 0;
|
||||||
|
bool Ab = ((OpCode >> 44) & 1) != 0;
|
||||||
|
bool Nb = ((OpCode >> 53) & 1) != 0;
|
||||||
|
bool Aa = ((OpCode >> 54) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: OperB = GetOperImm19_20 (OpCode); break;
|
||||||
|
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrInst CmpInst;
|
||||||
|
|
||||||
|
if (IsFloat)
|
||||||
|
{
|
||||||
|
OperA = GetAluAbsNeg(OperA, Aa, Na);
|
||||||
|
OperB = GetAluAbsNeg(OperB, Ab, Nb);
|
||||||
|
|
||||||
|
CmpInst = GetCmpF(OpCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CmpInst = GetCmp(OpCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);
|
||||||
|
|
||||||
|
ShaderIrInst LopInst = GetBLop(OpCode);
|
||||||
|
|
||||||
|
ShaderIrOperPred PNode = GetOperPred39(OpCode);
|
||||||
|
|
||||||
|
ShaderIrOperImmf Imm0 = new ShaderIrOperImmf(0);
|
||||||
|
ShaderIrOperImmf Imm1 = new ShaderIrOperImmf(1);
|
||||||
|
|
||||||
|
ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0);
|
||||||
|
ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1);
|
||||||
|
|
||||||
|
if (LopInst != ShaderIrInst.Band || !PNode.IsConst)
|
||||||
|
{
|
||||||
|
ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode);
|
||||||
|
|
||||||
|
Asg0 = new ShaderIrCond(Op2, Asg0, Not: true);
|
||||||
|
Asg1 = new ShaderIrCond(Op2, Asg1, Not: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Asg0 = new ShaderIrCond(Op, Asg0, Not: true);
|
||||||
|
Asg1 = new ShaderIrCond(Op, Asg1, Not: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(Asg0, OpCode));
|
||||||
|
Block.AddNode(GetPredNode(Asg1, OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
{
|
{
|
||||||
EmitSetp(Block, OpCode, true, Oper);
|
EmitSetp(Block, OpCode, true, Oper);
|
||||||
|
|
|
@ -70,6 +70,21 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
EmitI2f(Block, OpCode, ShaderOper.RR);
|
EmitI2f(Block, OpCode, ShaderOper.RR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void I2i_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitI2i(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I2i_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitI2i(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void I2i_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitI2i(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Mov_C(ShaderIrBlock Block, long OpCode)
|
public static void Mov_C(ShaderIrBlock Block, long OpCode)
|
||||||
{
|
{
|
||||||
ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
|
ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
|
||||||
|
@ -183,7 +198,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin);
|
ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin);
|
||||||
ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax);
|
ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax);
|
||||||
|
|
||||||
OperA = new ShaderIrOp(ShaderIrInst.Clamp, OperA, IMin, IMax);
|
OperA = new ShaderIrOp(ShaderIrInst.Fclamp, OperA, IMin, IMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderIrInst Inst = Signed
|
ShaderIrInst Inst = Signed
|
||||||
|
@ -252,6 +267,81 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitI2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
IntType Type = GetIntType(OpCode);
|
||||||
|
|
||||||
|
if (Type == IntType.U64 ||
|
||||||
|
Type == IntType.S64)
|
||||||
|
{
|
||||||
|
//TODO: 64-bits support.
|
||||||
|
//Note: GLSL doesn't support 64-bits integers.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sel = (int)(OpCode >> 41) & 3;
|
||||||
|
|
||||||
|
bool NegA = ((OpCode >> 45) & 1) != 0;
|
||||||
|
bool AbsA = ((OpCode >> 49) & 1) != 0;
|
||||||
|
bool SatA = ((OpCode >> 50) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrNode OperA;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
OperA = GetAluAbsNeg(OperA, AbsA, NegA);
|
||||||
|
|
||||||
|
bool Signed = Type >= IntType.S8;
|
||||||
|
|
||||||
|
int Shift = Sel * 8;
|
||||||
|
|
||||||
|
int Size = 8 << ((int)Type & 3);
|
||||||
|
|
||||||
|
if (Shift != 0)
|
||||||
|
{
|
||||||
|
OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Size < 32)
|
||||||
|
{
|
||||||
|
uint Mask = uint.MaxValue >> (32 - Size);
|
||||||
|
|
||||||
|
if (SatA)
|
||||||
|
{
|
||||||
|
uint CMin = 0;
|
||||||
|
uint CMax = Mask;
|
||||||
|
|
||||||
|
if (Signed)
|
||||||
|
{
|
||||||
|
uint HalfMask = Mask >> 1;
|
||||||
|
|
||||||
|
CMin -= HalfMask + 1;
|
||||||
|
CMax = HalfMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrOperImm IMin = new ShaderIrOperImm((int)CMin);
|
||||||
|
ShaderIrOperImm IMax = new ShaderIrOperImm((int)CMax);
|
||||||
|
|
||||||
|
OperA = new ShaderIrOp(Signed
|
||||||
|
? ShaderIrInst.Clamps
|
||||||
|
: ShaderIrInst.Clampu, OperA, IMin, IMax);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
private static IntType GetIntType(long OpCode)
|
private static IntType GetIntType(long OpCode)
|
||||||
{
|
{
|
||||||
bool Signed = ((OpCode >> 13) & 1) != 0;
|
bool Signed = ((OpCode >> 13) & 1) != 0;
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
F_Start,
|
F_Start,
|
||||||
Ceil,
|
Ceil,
|
||||||
Clamp,
|
|
||||||
Fabs,
|
Fabs,
|
||||||
Fadd,
|
Fadd,
|
||||||
Fceq,
|
Fceq,
|
||||||
|
@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Fcgeu,
|
Fcgeu,
|
||||||
Fcgt,
|
Fcgt,
|
||||||
Fcgtu,
|
Fcgtu,
|
||||||
|
Fclamp,
|
||||||
Fcle,
|
Fcle,
|
||||||
Fcleu,
|
Fcleu,
|
||||||
Fclt,
|
Fclt,
|
||||||
|
@ -53,6 +54,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Ceq,
|
Ceq,
|
||||||
Cge,
|
Cge,
|
||||||
Cgt,
|
Cgt,
|
||||||
|
Clamps,
|
||||||
|
Clampu,
|
||||||
Cle,
|
Cle,
|
||||||
Clt,
|
Clt,
|
||||||
Cne,
|
Cne,
|
||||||
|
|
|
@ -6,11 +6,24 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
private const int EncodingBits = 14;
|
private const int EncodingBits = 14;
|
||||||
|
|
||||||
private static ShaderDecodeFunc[] OpCodes;
|
private class ShaderDecodeEntry
|
||||||
|
{
|
||||||
|
public ShaderDecodeFunc Func;
|
||||||
|
|
||||||
|
public int XBits;
|
||||||
|
|
||||||
|
public ShaderDecodeEntry(ShaderDecodeFunc Func, int XBits)
|
||||||
|
{
|
||||||
|
this.Func = Func;
|
||||||
|
this.XBits = XBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ShaderDecodeEntry[] OpCodes;
|
||||||
|
|
||||||
static ShaderOpCodeTable()
|
static ShaderOpCodeTable()
|
||||||
{
|
{
|
||||||
OpCodes = new ShaderDecodeFunc[1 << EncodingBits];
|
OpCodes = new ShaderDecodeEntry[1 << EncodingBits];
|
||||||
|
|
||||||
#region Instructions
|
#region Instructions
|
||||||
Set("111000110000xx", ShaderDecode.Exit);
|
Set("111000110000xx", ShaderDecode.Exit);
|
||||||
|
@ -31,12 +44,18 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Set("0100110001101x", ShaderDecode.Fmul_C);
|
Set("0100110001101x", ShaderDecode.Fmul_C);
|
||||||
Set("0011100x01101x", ShaderDecode.Fmul_I);
|
Set("0011100x01101x", ShaderDecode.Fmul_I);
|
||||||
Set("0101110001101x", ShaderDecode.Fmul_R);
|
Set("0101110001101x", ShaderDecode.Fmul_R);
|
||||||
|
Set("0100100xxxxxxx", ShaderDecode.Fset_C);
|
||||||
|
Set("0011000xxxxxxx", ShaderDecode.Fset_I);
|
||||||
|
Set("01011000xxxxxx", ShaderDecode.Fset_R);
|
||||||
Set("010010111011xx", ShaderDecode.Fsetp_C);
|
Set("010010111011xx", ShaderDecode.Fsetp_C);
|
||||||
Set("0011011x1011xx", ShaderDecode.Fsetp_I);
|
Set("0011011x1011xx", ShaderDecode.Fsetp_I);
|
||||||
Set("010110111011xx", ShaderDecode.Fsetp_R);
|
Set("010110111011xx", ShaderDecode.Fsetp_R);
|
||||||
Set("0100110010111x", ShaderDecode.I2f_C);
|
Set("0100110010111x", ShaderDecode.I2f_C);
|
||||||
Set("0011100x10111x", ShaderDecode.I2f_I);
|
Set("0011100x10111x", ShaderDecode.I2f_I);
|
||||||
Set("0101110010111x", ShaderDecode.I2f_R);
|
Set("0101110010111x", ShaderDecode.I2f_R);
|
||||||
|
Set("0100110011100x", ShaderDecode.I2i_C);
|
||||||
|
Set("0011100x11100x", ShaderDecode.I2i_I);
|
||||||
|
Set("0101110011100x", ShaderDecode.I2i_R);
|
||||||
Set("11100000xxxxxx", ShaderDecode.Ipa);
|
Set("11100000xxxxxx", ShaderDecode.Ipa);
|
||||||
Set("010010110110xx", ShaderDecode.Isetp_C);
|
Set("010010110110xx", ShaderDecode.Isetp_C);
|
||||||
Set("0011011x0110xx", ShaderDecode.Isetp_I);
|
Set("0011011x0110xx", ShaderDecode.Isetp_I);
|
||||||
|
@ -91,6 +110,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
XMask = ~XMask;
|
XMask = ~XMask;
|
||||||
|
|
||||||
|
ShaderDecodeEntry Entry = new ShaderDecodeEntry(Func, XBits);
|
||||||
|
|
||||||
for (int Index = 0; Index < (1 << XBits); Index++)
|
for (int Index = 0; Index < (1 << XBits); Index++)
|
||||||
{
|
{
|
||||||
Value &= XMask;
|
Value &= XMask;
|
||||||
|
@ -100,13 +121,16 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Value |= ((Index >> X) & 1) << XPos[X];
|
Value |= ((Index >> X) & 1) << XPos[X];
|
||||||
}
|
}
|
||||||
|
|
||||||
OpCodes[Value] = Func;
|
if (OpCodes[Value] == null || OpCodes[Value].XBits > XBits)
|
||||||
|
{
|
||||||
|
OpCodes[Value] = Entry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShaderDecodeFunc GetDecoder(long OpCode)
|
public static ShaderDecodeFunc GetDecoder(long OpCode)
|
||||||
{
|
{
|
||||||
return OpCodes[(ulong)OpCode >> (64 - EncodingBits)];
|
return OpCodes[(ulong)OpCode >> (64 - EncodingBits)]?.Func;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,10 +7,11 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
public IGalRenderer Renderer { get; private set; }
|
public IGalRenderer Renderer { get; private set; }
|
||||||
|
|
||||||
internal NsGpuMemoryMgr MemoryMgr { get; private set; }
|
public NsGpuMemoryMgr MemoryMgr { get; private set; }
|
||||||
|
|
||||||
public NvGpuFifo Fifo { get; private set; }
|
public NvGpuFifo Fifo { get; private set; }
|
||||||
|
|
||||||
|
public NvGpuEngine2d Engine2d { get; private set; }
|
||||||
public NvGpuEngine3d Engine3d { get; private set; }
|
public NvGpuEngine3d Engine3d { get; private set; }
|
||||||
|
|
||||||
private Thread FifoProcessing;
|
private Thread FifoProcessing;
|
||||||
|
@ -25,6 +26,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
Fifo = new NvGpuFifo(this);
|
Fifo = new NvGpuFifo(this);
|
||||||
|
|
||||||
|
Engine2d = new NvGpuEngine2d(this);
|
||||||
Engine3d = new NvGpuEngine3d(this);
|
Engine3d = new NvGpuEngine3d(this);
|
||||||
|
|
||||||
KeepRunning = true;
|
KeepRunning = true;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
class NsGpuMemoryMgr
|
public class NsGpuMemoryMgr
|
||||||
{
|
{
|
||||||
private const long AddrSize = 1L << 40;
|
private const long AddrSize = 1L << 40;
|
||||||
|
|
||||||
|
@ -50,6 +50,14 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
return GpuAddr;
|
return GpuAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Unmap(long Position, long Size)
|
||||||
|
{
|
||||||
|
for (long Offset = 0; Offset < Size; Offset += PageSize)
|
||||||
|
{
|
||||||
|
SetPTAddr(Position + Offset, PteUnmapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public long Map(long CpuAddr, long Size)
|
public long Map(long CpuAddr, long Size)
|
||||||
{
|
{
|
||||||
CpuAddr &= ~PageMask;
|
CpuAddr &= ~PageMask;
|
||||||
|
|
158
Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs
Normal file
158
Ryujinx.Graphics/Gpu/NvGpuEngine2d.cs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
public class NvGpuEngine2d : INvGpuEngine
|
||||||
|
{
|
||||||
|
private enum CopyOperation
|
||||||
|
{
|
||||||
|
SrcCopyAnd,
|
||||||
|
RopAnd,
|
||||||
|
Blend,
|
||||||
|
SrcCopy,
|
||||||
|
Rop,
|
||||||
|
SrcCopyPremult,
|
||||||
|
BlendPremult
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] Registers { get; private set; }
|
||||||
|
|
||||||
|
private NsGpu Gpu;
|
||||||
|
|
||||||
|
private Dictionary<int, NvGpuMethod> Methods;
|
||||||
|
|
||||||
|
public NvGpuEngine2d(NsGpu Gpu)
|
||||||
|
{
|
||||||
|
this.Gpu = Gpu;
|
||||||
|
|
||||||
|
Registers = new int[0xe00];
|
||||||
|
|
||||||
|
Methods = new Dictionary<int, NvGpuMethod>();
|
||||||
|
|
||||||
|
void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
|
||||||
|
{
|
||||||
|
while (Count-- > 0)
|
||||||
|
{
|
||||||
|
Methods.Add(Meth, Method);
|
||||||
|
|
||||||
|
Meth += Stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddMethod(0xb5, 1, 1, TextureCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
|
||||||
|
{
|
||||||
|
Method(Memory, PBEntry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteRegister(PBEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TextureCopy(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation);
|
||||||
|
|
||||||
|
bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0;
|
||||||
|
int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth);
|
||||||
|
int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight);
|
||||||
|
|
||||||
|
bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0;
|
||||||
|
int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth);
|
||||||
|
int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight);
|
||||||
|
int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch);
|
||||||
|
int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions);
|
||||||
|
|
||||||
|
TextureSwizzle DstSwizzle = DstLinear
|
||||||
|
? TextureSwizzle.Pitch
|
||||||
|
: TextureSwizzle.BlockLinear;
|
||||||
|
|
||||||
|
int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
|
||||||
|
|
||||||
|
long Tag = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress);
|
||||||
|
|
||||||
|
TryGetCpuAddr(NvGpuEngine2dReg.SrcAddress, out long SrcAddress);
|
||||||
|
TryGetCpuAddr(NvGpuEngine2dReg.DstAddress, out long DstAddress);
|
||||||
|
|
||||||
|
bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag);
|
||||||
|
|
||||||
|
if (IsFbTexture && DstLinear)
|
||||||
|
{
|
||||||
|
DstSwizzle = TextureSwizzle.BlockLinear;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture DstTexture = new Texture(
|
||||||
|
DstAddress,
|
||||||
|
DstWidth,
|
||||||
|
DstHeight,
|
||||||
|
DstBlockHeight,
|
||||||
|
DstBlockHeight,
|
||||||
|
DstSwizzle,
|
||||||
|
GalTextureFormat.A8B8G8R8);
|
||||||
|
|
||||||
|
if (IsFbTexture)
|
||||||
|
{
|
||||||
|
Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) =>
|
||||||
|
{
|
||||||
|
CopyTexture(Memory, DstTexture, Buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long Size = SrcWidth * SrcHeight * 4;
|
||||||
|
|
||||||
|
byte[] Buffer = AMemoryHelper.ReadBytes(Memory, SrcAddress, Size);
|
||||||
|
|
||||||
|
CopyTexture(Memory, DstTexture, Buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyTexture(AMemory Memory, Texture Texture, byte[] Buffer)
|
||||||
|
{
|
||||||
|
TextureWriter.Write(Memory, Texture, Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCpuAddr(NvGpuEngine2dReg Reg, out long Position)
|
||||||
|
{
|
||||||
|
Position = MakeInt64From2xInt32(Reg);
|
||||||
|
|
||||||
|
Position = Gpu.GetCpuAddr(Position);
|
||||||
|
|
||||||
|
return Position != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(long)Registers[(int)Reg + 0] << 32 |
|
||||||
|
(uint)Registers[(int)Reg + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteRegister(NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
int ArgsCount = PBEntry.Arguments.Count;
|
||||||
|
|
||||||
|
if (ArgsCount > 0)
|
||||||
|
{
|
||||||
|
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ReadRegister(NvGpuEngine2dReg Reg)
|
||||||
|
{
|
||||||
|
return Registers[(int)Reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteRegister(NvGpuEngine2dReg Reg, int Value)
|
||||||
|
{
|
||||||
|
Registers[(int)Reg] = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs
Normal file
25
Ryujinx.Graphics/Gpu/NvGpuEngine2dReg.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
enum NvGpuEngine2dReg
|
||||||
|
{
|
||||||
|
DstFormat = 0x80,
|
||||||
|
DstLinear = 0x81,
|
||||||
|
DstBlockDimensions = 0x82,
|
||||||
|
DstDepth = 0x83,
|
||||||
|
DstLayer = 0x84,
|
||||||
|
DstPitch = 0x85,
|
||||||
|
DstWidth = 0x86,
|
||||||
|
DstHeight = 0x87,
|
||||||
|
DstAddress = 0x88,
|
||||||
|
SrcFormat = 0x8c,
|
||||||
|
SrcLinear = 0x8d,
|
||||||
|
SrcBlockDimensions = 0x8e,
|
||||||
|
SrcDepth = 0x8f,
|
||||||
|
SrcLayer = 0x90,
|
||||||
|
SrcPitch = 0x91,
|
||||||
|
SrcWidth = 0x92,
|
||||||
|
SrcHeight = 0x93,
|
||||||
|
SrcAddress = 0x94,
|
||||||
|
CopyOperation = 0xab
|
||||||
|
}
|
||||||
|
}
|
|
@ -360,6 +360,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
for (int Index = 0; Index < 32; Index++)
|
for (int Index = 0; Index < 32; Index++)
|
||||||
{
|
{
|
||||||
|
int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst);
|
||||||
|
int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount);
|
||||||
|
|
||||||
int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
|
int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
|
||||||
|
|
||||||
bool Enable = (Control & 0x1000) != 0;
|
bool Enable = (Control & 0x1000) != 0;
|
||||||
|
@ -394,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Gpu.Renderer.DrawArrays(Index, PrimType);
|
Gpu.Renderer.DrawArrays(Index, VertexFirst, VertexCount, PrimType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
ViewportTranslateX = 0x283,
|
ViewportTranslateX = 0x283,
|
||||||
ViewportTranslateY = 0x284,
|
ViewportTranslateY = 0x284,
|
||||||
ViewportTranslateZ = 0x285,
|
ViewportTranslateZ = 0x285,
|
||||||
|
VertexArrayFirst = 0x35d,
|
||||||
|
VertexArrayCount = 0x35e,
|
||||||
VertexAttribNFormat = 0x458,
|
VertexAttribNFormat = 0x458,
|
||||||
IBlendEnable = 0x4b9,
|
IBlendEnable = 0x4b9,
|
||||||
BlendSeparateAlpha = 0x4cf,
|
BlendSeparateAlpha = 0x4cf,
|
||||||
|
|
|
@ -139,11 +139,17 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
switch (SubChannels[PBEntry.SubChannel])
|
switch (SubChannels[PBEntry.SubChannel])
|
||||||
{
|
{
|
||||||
|
case NvGpuEngine._2d: Call2dMethod(Memory, PBEntry); break;
|
||||||
case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break;
|
case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Call2dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
{
|
||||||
|
Gpu.Engine2d.CallMethod(Memory, PBEntry);
|
||||||
|
}
|
||||||
|
|
||||||
private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
{
|
{
|
||||||
if (PBEntry.Method < 0xe00)
|
if (PBEntry.Method < 0xe00)
|
||||||
|
|
23
Ryujinx.Graphics/Gpu/TextureHelper.cs
Normal file
23
Ryujinx.Graphics/Gpu/TextureHelper.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
static class TextureHelper
|
||||||
|
{
|
||||||
|
public static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp)
|
||||||
|
{
|
||||||
|
switch (Texture.Swizzle)
|
||||||
|
{
|
||||||
|
case TextureSwizzle.Pitch:
|
||||||
|
case TextureSwizzle.PitchColorKey:
|
||||||
|
return new LinearSwizzle(Texture.Pitch, Bpp);
|
||||||
|
|
||||||
|
case TextureSwizzle.BlockLinear:
|
||||||
|
case TextureSwizzle.BlockLinearColorKey:
|
||||||
|
return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException(Texture.Swizzle.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
byte[] Output = new byte[Width * Height * 2];
|
byte[] Output = new byte[Width * Height * 2];
|
||||||
|
|
||||||
ISwizzle Swizzle = GetSwizzle(Texture, Width, 2);
|
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2);
|
||||||
|
|
||||||
fixed (byte* BuffPtr = Output)
|
fixed (byte* BuffPtr = Output)
|
||||||
{
|
{
|
||||||
|
@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
byte[] Output = new byte[Width * Height * 4];
|
byte[] Output = new byte[Width * Height * 4];
|
||||||
|
|
||||||
ISwizzle Swizzle = GetSwizzle(Texture, Width, 4);
|
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);
|
||||||
|
|
||||||
fixed (byte* BuffPtr = Output)
|
fixed (byte* BuffPtr = Output)
|
||||||
{
|
{
|
||||||
|
@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
byte[] Output = new byte[Width * Height * 8];
|
byte[] Output = new byte[Width * Height * 8];
|
||||||
|
|
||||||
ISwizzle Swizzle = GetSwizzle(Texture, Width, 8);
|
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8);
|
||||||
|
|
||||||
fixed (byte* BuffPtr = Output)
|
fixed (byte* BuffPtr = Output)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
byte[] Output = new byte[Width * Height * 16];
|
byte[] Output = new byte[Width * Height * 16];
|
||||||
|
|
||||||
ISwizzle Swizzle = GetSwizzle(Texture, Width, 16);
|
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16);
|
||||||
|
|
||||||
fixed (byte* BuffPtr = Output)
|
fixed (byte* BuffPtr = Output)
|
||||||
{
|
{
|
||||||
|
@ -140,21 +140,5 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
return Output;
|
return Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp)
|
|
||||||
{
|
|
||||||
switch (Texture.Swizzle)
|
|
||||||
{
|
|
||||||
case TextureSwizzle.Pitch:
|
|
||||||
case TextureSwizzle.PitchColorKey:
|
|
||||||
return new LinearSwizzle(Texture.Pitch, Bpp);
|
|
||||||
|
|
||||||
case TextureSwizzle.BlockLinear:
|
|
||||||
case TextureSwizzle.BlockLinearColorKey:
|
|
||||||
return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotImplementedException(Texture.Swizzle.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
Ryujinx.Graphics/Gpu/TextureWriter.cs
Normal file
45
Ryujinx.Graphics/Gpu/TextureWriter.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
public static class TextureWriter
|
||||||
|
{
|
||||||
|
public static void Write(AMemory Memory, Texture Texture, byte[] Data)
|
||||||
|
{
|
||||||
|
switch (Texture.Format)
|
||||||
|
{
|
||||||
|
case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(Texture.Format.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe static void Write4Bpp(AMemory Memory, Texture Texture, byte[] Data)
|
||||||
|
{
|
||||||
|
int Width = Texture.Width;
|
||||||
|
int Height = Texture.Height;
|
||||||
|
|
||||||
|
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);
|
||||||
|
|
||||||
|
fixed (byte* BuffPtr = Data)
|
||||||
|
{
|
||||||
|
long InOffs = 0;
|
||||||
|
|
||||||
|
for (int Y = 0; Y < Height; Y++)
|
||||||
|
for (int X = 0; X < Width; X++)
|
||||||
|
{
|
||||||
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
|
int Pixel = *(int*)(BuffPtr + InOffs);
|
||||||
|
|
||||||
|
Memory.WriteInt32Unchecked(Texture.Position + Offset, Pixel);
|
||||||
|
|
||||||
|
InOffs += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,14 +22,11 @@ namespace Ryujinx
|
||||||
|
|
||||||
AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
|
AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
|
||||||
|
|
||||||
Console.WriteLine(Parser.Value("Logging_Enable_Warn"));
|
Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));
|
||||||
|
Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub")));
|
||||||
bool LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
|
Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info")));
|
||||||
bool LoggingEnableStub = Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"));
|
Log.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn")));
|
||||||
bool LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
|
Log.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error")));
|
||||||
bool LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
|
|
||||||
bool LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
|
|
||||||
bool LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
|
|
||||||
|
|
||||||
string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries);
|
string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
|
Reference in a new issue