More work on NvHostCtrl
This commit is contained in:
parent
d27f1f4607
commit
f992843b6d
5 changed files with 197 additions and 190 deletions
|
@ -11,13 +11,13 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
|
||||
private static ConcurrentDictionary<Process, NvHostEvent[]> EventArrays;
|
||||
|
||||
private static ConcurrentDictionary<Process, IdDictionary> NvSyncPts;
|
||||
private static ConcurrentDictionary<Process, NvHostSyncPt> NvSyncPts;
|
||||
|
||||
static NvHostCtrlIoctl()
|
||||
{
|
||||
EventArrays = new ConcurrentDictionary<Process, NvHostEvent[]>();
|
||||
|
||||
NvSyncPts = new ConcurrentDictionary<Process, IdDictionary>();
|
||||
NvSyncPts = new ConcurrentDictionary<Process, NvHostSyncPt>();
|
||||
}
|
||||
|
||||
private int SyncPtRead(ServiceCtx Context)
|
||||
|
@ -31,7 +31,12 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
|
||||
int Id = Context.Memory.ReadInt32(InputPosition);
|
||||
|
||||
GetOrAddNvSyncPt(Context, Id).Increment();
|
||||
if ((uint)Id >= NvHostSyncPt.SyncPtsCount)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
GetSyncPt(Context).Increment(Id);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
@ -51,6 +56,16 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
return SyncPtReadMinOrMax(Context, Max: true);
|
||||
}
|
||||
|
||||
private int EventWait(ServiceCtx Context)
|
||||
{
|
||||
return EventWait(Context, Async: false);
|
||||
}
|
||||
|
||||
private int EventWaitAsync(ServiceCtx Context)
|
||||
{
|
||||
return EventWait(Context, Async: true);
|
||||
}
|
||||
|
||||
private int SyncPtReadMinOrMax(ServiceCtx Context, bool Max)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
|
@ -58,13 +73,18 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
|
||||
NvHostCtrlSyncPtRead Args = AMemoryHelper.Read<NvHostCtrlSyncPtRead>(Context.Memory, InputPosition);
|
||||
|
||||
if ((uint)Args.Id >= NvHostSyncPt.SyncPtsCount)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
if (Max)
|
||||
{
|
||||
Args.Value = GetOrAddNvSyncPt(Context, Args.Id).CounterMax;
|
||||
Args.Value = GetSyncPt(Context).GetMax(Args.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Args.Value = GetOrAddNvSyncPt(Context, Args.Id).CounterMin;
|
||||
Args.Value = GetSyncPt(Context).GetMin(Args.Id);
|
||||
}
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
@ -79,213 +99,171 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
|
||||
NvHostCtrlSyncPtWait Args = AMemoryHelper.Read<NvHostCtrlSyncPtWait>(Context.Memory, InputPosition);
|
||||
|
||||
NvSyncPt SyncPt = GetOrAddNvSyncPt(Context, Args.Id);
|
||||
NvHostSyncPt SyncPt = GetSyncPt(Context);
|
||||
|
||||
void WriteSyncPtMin()
|
||||
if ((uint)Args.Id >= NvHostSyncPt.SyncPtsCount)
|
||||
{
|
||||
if (Extended)
|
||||
{
|
||||
Context.Memory.WriteInt32(OutputPosition + 0xc, SyncPt.CounterMin);
|
||||
}
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
if (SyncPt.MinCompare(Args.Thresh))
|
||||
{
|
||||
WriteSyncPtMin();
|
||||
int Result;
|
||||
|
||||
return NvResult.Success;
|
||||
if (SyncPt.MinCompare(Args.Id, Args.Thresh))
|
||||
{
|
||||
Result = NvResult.Success;
|
||||
}
|
||||
|
||||
if (Args.Timeout == 0)
|
||||
else if (Args.Timeout == 0)
|
||||
{
|
||||
return NvResult.TryAgain;
|
||||
}
|
||||
|
||||
Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms...");
|
||||
|
||||
bool Signaled = true;
|
||||
|
||||
using (ManualResetEvent WaitEvent = new ManualResetEvent(false))
|
||||
{
|
||||
SyncPt.AddWaiter(Args.Thresh, WaitEvent);
|
||||
|
||||
if (Args.Timeout != -1)
|
||||
{
|
||||
//Note: Negative (> INT_MAX) timeouts aren't valid on .NET,
|
||||
//in this case we just use the maximum timeout possible.
|
||||
int Timeout = Args.Timeout;
|
||||
|
||||
if (Timeout < 0)
|
||||
{
|
||||
Timeout = int.MaxValue;
|
||||
}
|
||||
|
||||
Signaled = WaitEvent.WaitOne(Timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
WriteSyncPtMin();
|
||||
|
||||
if (Signaled)
|
||||
{
|
||||
Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Resuming...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Timed out, resuming...");
|
||||
}
|
||||
|
||||
return Signaled
|
||||
? NvResult.Success
|
||||
: NvResult.TimedOut;
|
||||
}
|
||||
|
||||
private int EventWait(ServiceCtx Context, bool IsEvent)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
int Result, Value;
|
||||
|
||||
NvHostCtrlSyncPtWait Args = AMemoryHelper.Read<NvHostCtrlSyncPtWait>(Context.Memory, InputPosition);
|
||||
|
||||
if (Args.Timeout == 0)
|
||||
{
|
||||
return NvResult.TryAgain;
|
||||
}
|
||||
|
||||
if (IsEvent || Args.Timeout == -1)
|
||||
{
|
||||
int EventIndex = GetFreeEvent(Context, Args.Id, out NvHostEvent Event);
|
||||
|
||||
if (EventIndex == EventsCount)
|
||||
{
|
||||
return NvResult.OutOfMemory;
|
||||
}
|
||||
|
||||
Event.Id = Args.Id;
|
||||
Event.Thresh = Args.Thresh;
|
||||
|
||||
Event.Free = false;
|
||||
|
||||
if (IsEvent)
|
||||
{
|
||||
Value = ((Args.Id & 0xfff) << 16) | 0x10000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = Args.Id << 4;
|
||||
}
|
||||
|
||||
Value |= EventIndex;
|
||||
|
||||
Result = NvResult.TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms...");
|
||||
|
||||
bool Signaled = true;
|
||||
|
||||
NvSyncPt SyncPt = GetOrAddNvSyncPt(Context, Args.Id);
|
||||
|
||||
using (ManualResetEvent WaitEvent = new ManualResetEvent(false))
|
||||
{
|
||||
SyncPt.AddWaiter(Args.Thresh, WaitEvent);
|
||||
|
||||
if (Args.Timeout != -1)
|
||||
//Note: Negative (> INT_MAX) timeouts aren't valid on .NET,
|
||||
//in this case we just use the maximum timeout possible.
|
||||
int Timeout = Args.Timeout;
|
||||
|
||||
if (Timeout < -1)
|
||||
{
|
||||
//Note: Negative (> INT_MAX) timeouts aren't valid on .NET,
|
||||
//in this case we just use the maximum timeout possible.
|
||||
int Timeout = Args.Timeout;
|
||||
Timeout = int.MaxValue;
|
||||
}
|
||||
|
||||
if (Timeout < 0)
|
||||
{
|
||||
Timeout = int.MaxValue;
|
||||
}
|
||||
if (Timeout == -1)
|
||||
{
|
||||
WaitEvent.WaitOne();
|
||||
|
||||
Signaled = WaitEvent.WaitOne(Timeout);
|
||||
Result = NvResult.Success;
|
||||
}
|
||||
else if (WaitEvent.WaitOne(Timeout))
|
||||
{
|
||||
Result = NvResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitEvent.WaitOne();
|
||||
Result = NvResult.TimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
if (Signaled)
|
||||
{
|
||||
Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Resuming...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Timed out, resuming...");
|
||||
}
|
||||
|
||||
Value = SyncPt.CounterMin;
|
||||
|
||||
Result = Signaled
|
||||
? NvResult.Success
|
||||
: NvResult.TimedOut;
|
||||
}
|
||||
|
||||
Context.Memory.WriteInt32(OutputPosition + 0xc, Value);
|
||||
if (Extended)
|
||||
{
|
||||
Context.Memory.WriteInt32(OutputPosition + 0xc, SyncPt.GetMin(Args.Id));
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private NvSyncPt GetOrAddNvSyncPt(ServiceCtx Context, int Id)
|
||||
private int EventWait(ServiceCtx Context, bool Async)
|
||||
{
|
||||
NvSyncPt SyncPt = GetNvSyncPt(Context, Id);
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
if (SyncPt == null)
|
||||
int Result;
|
||||
|
||||
NvHostCtrlSyncPtWaitEx Args = AMemoryHelper.Read<NvHostCtrlSyncPtWaitEx>(Context.Memory, InputPosition);
|
||||
|
||||
if ((uint)Args.Id >= NvHostSyncPt.SyncPtsCount)
|
||||
{
|
||||
IdDictionary SyncPts = NvSyncPts.GetOrAdd(Context.Process, (Key) => new IdDictionary());
|
||||
|
||||
SyncPt = new NvSyncPt();
|
||||
|
||||
SyncPts.Add(Id, SyncPt);
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
return SyncPt;
|
||||
}
|
||||
NvHostSyncPt SyncPt = GetSyncPt(Context);
|
||||
|
||||
public NvSyncPt GetNvSyncPt(ServiceCtx Context, int Id)
|
||||
{
|
||||
if (NvSyncPts.TryGetValue(Context.Process, out IdDictionary SyncPts))
|
||||
if (SyncPt.MinCompare(Args.Id, Args.Thresh))
|
||||
{
|
||||
return SyncPts.GetData<NvSyncPt>(Id);
|
||||
Args.Value = SyncPt.GetMin(Args.Id);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
return null;
|
||||
if (Args.Timeout == 0)
|
||||
{
|
||||
if (!Async)
|
||||
{
|
||||
Args.Value = 0;
|
||||
}
|
||||
|
||||
return NvResult.TryAgain;
|
||||
}
|
||||
|
||||
NvHostEvent Event;
|
||||
|
||||
int EventIndex;
|
||||
|
||||
if (Async)
|
||||
{
|
||||
EventIndex = Args.Value;
|
||||
|
||||
if ((uint)EventIndex >= EventsCount)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
Event = GetEvents(Context)[EventIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
Event = GetFreeEvent(Context, SyncPt, Args.Id, out EventIndex);
|
||||
}
|
||||
|
||||
if (Event == null)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
Event.Id = Args.Id;
|
||||
Event.Thresh = Args.Thresh;
|
||||
|
||||
Event.State = NvHostEventState.Waiting;
|
||||
|
||||
if (!Async)
|
||||
{
|
||||
Args.Value = ((Args.Id & 0xfff) << 16) | 0x10000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
Args.Value = Args.Id << 4;
|
||||
}
|
||||
|
||||
Args.Value |= EventIndex;
|
||||
|
||||
Result = NvResult.TryAgain;
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private int GetFreeEvent(ServiceCtx Context, int Id, out NvHostEvent Event)
|
||||
private NvHostEvent GetFreeEvent(ServiceCtx Context, NvHostSyncPt SyncPt, int Id, out int EventIndex)
|
||||
{
|
||||
NvHostEvent[] Events = GetEvents(Context);
|
||||
|
||||
EventIndex = EventsCount;
|
||||
|
||||
int NullIndex = EventsCount;
|
||||
int FreeIndex = EventsCount;
|
||||
|
||||
for (int Index = 0; Index < EventsCount; Index++)
|
||||
{
|
||||
Event = Events[Index];
|
||||
NvHostEvent Event = Events[Index];
|
||||
|
||||
if (Event != null)
|
||||
{
|
||||
if (Event.Free && MinCompare(Context,
|
||||
Event.Id,
|
||||
Event.Thresh))
|
||||
if (Event.State == NvHostEventState.Registered ||
|
||||
Event.State == NvHostEventState.Free)
|
||||
{
|
||||
EventIndex = Index;
|
||||
|
||||
if (Event.Id == Id)
|
||||
{
|
||||
return Index;
|
||||
return Event;
|
||||
}
|
||||
|
||||
FreeIndex = Index;
|
||||
}
|
||||
}
|
||||
else if (NullIndex == EventsCount)
|
||||
|
@ -296,23 +274,25 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
|
||||
if (NullIndex < EventsCount)
|
||||
{
|
||||
Events[NullIndex] = Event = new NvHostEvent();
|
||||
EventIndex = NullIndex;
|
||||
|
||||
return NullIndex;
|
||||
return Events[NullIndex] = new NvHostEvent();
|
||||
}
|
||||
|
||||
Event = FreeIndex < EventsCount ? Events[FreeIndex] : null;
|
||||
if (EventIndex < EventsCount)
|
||||
{
|
||||
return Events[EventIndex];
|
||||
}
|
||||
|
||||
return FreeIndex;
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool MinCompare(ServiceCtx Context, int Id, int Threshold)
|
||||
public static NvHostSyncPt GetSyncPt(ServiceCtx Context)
|
||||
{
|
||||
//TODO
|
||||
return false;
|
||||
return NvSyncPts.GetOrAdd(Context.Process, (Key) => new NvHostSyncPt());
|
||||
}
|
||||
|
||||
private NvHostEvent[] GetEvents(ServiceCtx Context)
|
||||
private static NvHostEvent[] GetEvents(ServiceCtx Context)
|
||||
{
|
||||
return EventArrays.GetOrAdd(Context.Process, (Key) => new NvHostEvent[EventsCount]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvHostCtrlSyncPtWaitEx
|
||||
{
|
||||
public int Id;
|
||||
public int Thresh;
|
||||
public int Timeout;
|
||||
public int Value;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvHostEvent
|
||||
|
@ -10,6 +5,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
public int Id;
|
||||
public int Thresh;
|
||||
|
||||
public bool Free;
|
||||
public NvHostEventState State;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
enum NvHostEventState
|
||||
{
|
||||
Registered = 0,
|
||||
Waiting = 1,
|
||||
Busy = 2,
|
||||
Free = 5
|
||||
}
|
||||
}
|
|
@ -5,42 +5,54 @@ using System.Threading;
|
|||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvSyncPt
|
||||
class NvHostSyncPt
|
||||
{
|
||||
private int m_CounterMin;
|
||||
private int m_CounterMax;
|
||||
public const int SyncPtsCount = 192;
|
||||
|
||||
public int CounterMin => m_CounterMin;
|
||||
public int CounterMax => m_CounterMax;
|
||||
public int[] CounterMin;
|
||||
public int[] CounterMax;
|
||||
|
||||
public bool IsEvent { get; set; }
|
||||
private long EventMask;
|
||||
|
||||
private ConcurrentDictionary<EventWaitHandle, int> Waiters;
|
||||
|
||||
public NvSyncPt()
|
||||
public NvHostSyncPt()
|
||||
{
|
||||
CounterMin = new int[SyncPtsCount];
|
||||
CounterMax = new int[SyncPtsCount];
|
||||
|
||||
Waiters = new ConcurrentDictionary<EventWaitHandle, int>();
|
||||
}
|
||||
|
||||
public int Increment()
|
||||
public int GetMin(int Id)
|
||||
{
|
||||
Interlocked.Increment(ref m_CounterMax);
|
||||
|
||||
return IncrementMin();
|
||||
return CounterMin[Id];
|
||||
}
|
||||
|
||||
public int IncrementMin()
|
||||
public int GetMax(int Id)
|
||||
{
|
||||
int Value = Interlocked.Increment(ref m_CounterMin);
|
||||
return CounterMax[Id];
|
||||
}
|
||||
|
||||
WakeUpWaiters(Value);
|
||||
public int Increment(int Id)
|
||||
{
|
||||
Interlocked.Increment(ref CounterMax[Id]);
|
||||
|
||||
return IncrementMin(Id);
|
||||
}
|
||||
|
||||
public int IncrementMin(int Id)
|
||||
{
|
||||
int Value = Interlocked.Increment(ref CounterMin[Id]);
|
||||
|
||||
WakeUpWaiters(Id, Value);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
public int IncrementMax()
|
||||
public int IncrementMax(int Id)
|
||||
{
|
||||
return Interlocked.Increment(ref m_CounterMax);
|
||||
return Interlocked.Increment(ref CounterMax[Id]);
|
||||
}
|
||||
|
||||
public void AddWaiter(int Threshold, EventWaitHandle WaitEvent)
|
||||
|
@ -56,11 +68,11 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
return Waiters.TryRemove(WaitEvent, out _);
|
||||
}
|
||||
|
||||
private void WakeUpWaiters(int NewValue)
|
||||
private void WakeUpWaiters(int Id, int NewValue)
|
||||
{
|
||||
foreach (KeyValuePair<EventWaitHandle, int> KV in Waiters)
|
||||
{
|
||||
if (MinCompare(NewValue, m_CounterMax, KV.Value))
|
||||
if (MinCompare(Id, NewValue, CounterMax[Id], KV.Value))
|
||||
{
|
||||
KV.Key.Set();
|
||||
|
||||
|
@ -69,17 +81,17 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
}
|
||||
}
|
||||
|
||||
public bool MinCompare(int Threshold)
|
||||
public bool MinCompare(int Id, int Threshold)
|
||||
{
|
||||
return MinCompare(m_CounterMin, m_CounterMax, Threshold);
|
||||
return MinCompare(Id, CounterMin[Id], CounterMax[Id], Threshold);
|
||||
}
|
||||
|
||||
private bool MinCompare(int Min, int Max, int Threshold)
|
||||
private bool MinCompare(int Id, int Min, int Max, int Threshold)
|
||||
{
|
||||
int MinDiff = Min - Threshold;
|
||||
int MaxDiff = Max - Threshold;
|
||||
|
||||
if (IsEvent)
|
||||
if (((EventMask >> Id) & 1) != 0)
|
||||
{
|
||||
return MinDiff >= 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue