Initial implementation of NvMap/NvHostCtrl
This commit is contained in:
parent
5f0dd965bf
commit
d27f1f4607
16 changed files with 781 additions and 33 deletions
|
@ -1,31 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs
Normal file
10
Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
static class NvHelper
|
||||
{
|
||||
public static void Crash()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
320
Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
Normal file
320
Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
Normal file
|
@ -0,0 +1,320 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvHostCtrlIoctl
|
||||
{
|
||||
private const int EventsCount = 64;
|
||||
|
||||
private static ConcurrentDictionary<Process, NvHostEvent[]> EventArrays;
|
||||
|
||||
private static ConcurrentDictionary<Process, IdDictionary> NvSyncPts;
|
||||
|
||||
static NvHostCtrlIoctl()
|
||||
{
|
||||
EventArrays = new ConcurrentDictionary<Process, NvHostEvent[]>();
|
||||
|
||||
NvSyncPts = new ConcurrentDictionary<Process, IdDictionary>();
|
||||
}
|
||||
|
||||
private int SyncPtRead(ServiceCtx Context)
|
||||
{
|
||||
return SyncPtReadMinOrMax(Context, Max: false);
|
||||
}
|
||||
|
||||
private int SyncPtIncr(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
|
||||
int Id = Context.Memory.ReadInt32(InputPosition);
|
||||
|
||||
GetOrAddNvSyncPt(Context, Id).Increment();
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
private int SyncPtWait(ServiceCtx Context)
|
||||
{
|
||||
return SyncPtWait(Context, Extended: false);
|
||||
}
|
||||
|
||||
private int SyncPtWaitEx(ServiceCtx Context)
|
||||
{
|
||||
return SyncPtWait(Context, Extended: true);
|
||||
}
|
||||
|
||||
private int SyncPtReadMax(ServiceCtx Context)
|
||||
{
|
||||
return SyncPtReadMinOrMax(Context, Max: true);
|
||||
}
|
||||
|
||||
private int SyncPtReadMinOrMax(ServiceCtx Context, bool Max)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvHostCtrlSyncPtRead Args = AMemoryHelper.Read<NvHostCtrlSyncPtRead>(Context.Memory, InputPosition);
|
||||
|
||||
if (Max)
|
||||
{
|
||||
Args.Value = GetOrAddNvSyncPt(Context, Args.Id).CounterMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
Args.Value = GetOrAddNvSyncPt(Context, Args.Id).CounterMin;
|
||||
}
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
private int SyncPtWait(ServiceCtx Context, bool Extended)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvHostCtrlSyncPtWait Args = AMemoryHelper.Read<NvHostCtrlSyncPtWait>(Context.Memory, InputPosition);
|
||||
|
||||
NvSyncPt SyncPt = GetOrAddNvSyncPt(Context, Args.Id);
|
||||
|
||||
void WriteSyncPtMin()
|
||||
{
|
||||
if (Extended)
|
||||
{
|
||||
Context.Memory.WriteInt32(OutputPosition + 0xc, SyncPt.CounterMin);
|
||||
}
|
||||
}
|
||||
|
||||
if (SyncPt.MinCompare(Args.Thresh))
|
||||
{
|
||||
WriteSyncPtMin();
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
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 < 0)
|
||||
{
|
||||
Timeout = int.MaxValue;
|
||||
}
|
||||
|
||||
Signaled = WaitEvent.WaitOne(Timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private NvSyncPt GetOrAddNvSyncPt(ServiceCtx Context, int Id)
|
||||
{
|
||||
NvSyncPt SyncPt = GetNvSyncPt(Context, Id);
|
||||
|
||||
if (SyncPt == null)
|
||||
{
|
||||
IdDictionary SyncPts = NvSyncPts.GetOrAdd(Context.Process, (Key) => new IdDictionary());
|
||||
|
||||
SyncPt = new NvSyncPt();
|
||||
|
||||
SyncPts.Add(Id, SyncPt);
|
||||
}
|
||||
|
||||
return SyncPt;
|
||||
}
|
||||
|
||||
public NvSyncPt GetNvSyncPt(ServiceCtx Context, int Id)
|
||||
{
|
||||
if (NvSyncPts.TryGetValue(Context.Process, out IdDictionary SyncPts))
|
||||
{
|
||||
return SyncPts.GetData<NvSyncPt>(Id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int GetFreeEvent(ServiceCtx Context, int Id, out NvHostEvent Event)
|
||||
{
|
||||
NvHostEvent[] Events = GetEvents(Context);
|
||||
|
||||
int NullIndex = EventsCount;
|
||||
int FreeIndex = EventsCount;
|
||||
|
||||
for (int Index = 0; Index < EventsCount; Index++)
|
||||
{
|
||||
Event = Events[Index];
|
||||
|
||||
if (Event != null)
|
||||
{
|
||||
if (Event.Free && MinCompare(Context,
|
||||
Event.Id,
|
||||
Event.Thresh))
|
||||
{
|
||||
if (Event.Id == Id)
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
|
||||
FreeIndex = Index;
|
||||
}
|
||||
}
|
||||
else if (NullIndex == EventsCount)
|
||||
{
|
||||
NullIndex = Index;
|
||||
}
|
||||
}
|
||||
|
||||
if (NullIndex < EventsCount)
|
||||
{
|
||||
Events[NullIndex] = Event = new NvHostEvent();
|
||||
|
||||
return NullIndex;
|
||||
}
|
||||
|
||||
Event = FreeIndex < EventsCount ? Events[FreeIndex] : null;
|
||||
|
||||
return FreeIndex;
|
||||
}
|
||||
|
||||
private bool MinCompare(ServiceCtx Context, int Id, int Threshold)
|
||||
{
|
||||
//TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
private NvHostEvent[] GetEvents(ServiceCtx Context)
|
||||
{
|
||||
return EventArrays.GetOrAdd(Context.Process, (Key) => new NvHostEvent[EventsCount]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvHostCtrlSyncPtRead
|
||||
{
|
||||
public int Id;
|
||||
public int Value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvHostCtrlSyncPtWait
|
||||
{
|
||||
public int Id;
|
||||
public int Thresh;
|
||||
public int Timeout;
|
||||
}
|
||||
}
|
15
Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs
Normal file
15
Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvHostEvent
|
||||
{
|
||||
public int Id;
|
||||
public int Thresh;
|
||||
|
||||
public bool Free;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvMap
|
||||
|
@ -10,11 +12,28 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
|||
public long CpuAddress;
|
||||
public long GpuAddress;
|
||||
|
||||
public NvMap() { }
|
||||
private long m_RefCount;
|
||||
|
||||
public NvMap(int Size)
|
||||
public long RefCount => m_RefCount;
|
||||
|
||||
public NvMap()
|
||||
{
|
||||
m_RefCount = 1;
|
||||
}
|
||||
|
||||
public NvMap(int Size) : this()
|
||||
{
|
||||
this.Size = Size;
|
||||
}
|
||||
|
||||
public long IncrementRefCount()
|
||||
{
|
||||
return Interlocked.Increment(ref m_RefCount);
|
||||
}
|
||||
|
||||
public long DecrementRefCount()
|
||||
{
|
||||
return Interlocked.Decrement(ref m_RefCount);
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/Services/Nv/NvMapAlloc.cs
Normal file
12
Ryujinx.Core/OsHle/Services/Nv/NvMapAlloc.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvMapAlloc
|
||||
{
|
||||
public int Handle;
|
||||
public int HeapMask;
|
||||
public int Flags;
|
||||
public int Align;
|
||||
public long Kind;
|
||||
public long Address;
|
||||
}
|
||||
}
|
8
Ryujinx.Core/OsHle/Services/Nv/NvMapCreate.cs
Normal file
8
Ryujinx.Core/OsHle/Services/Nv/NvMapCreate.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvMapCreate
|
||||
{
|
||||
public int Size;
|
||||
public int Handle;
|
||||
}
|
||||
}
|
11
Ryujinx.Core/OsHle/Services/Nv/NvMapFree.cs
Normal file
11
Ryujinx.Core/OsHle/Services/Nv/NvMapFree.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvMapFree
|
||||
{
|
||||
public int Handle;
|
||||
public int Padding;
|
||||
public long RefCount;
|
||||
public int Size;
|
||||
public int Flags;
|
||||
}
|
||||
}
|
8
Ryujinx.Core/OsHle/Services/Nv/NvMapFromId.cs
Normal file
8
Ryujinx.Core/OsHle/Services/Nv/NvMapFromId.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvMapFromId
|
||||
{
|
||||
public int Id;
|
||||
public int Handle;
|
||||
}
|
||||
}
|
8
Ryujinx.Core/OsHle/Services/Nv/NvMapGetId.cs
Normal file
8
Ryujinx.Core/OsHle/Services/Nv/NvMapGetId.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvMapGetId
|
||||
{
|
||||
public int Id;
|
||||
public int Handle;
|
||||
}
|
||||
}
|
238
Ryujinx.Core/OsHle/Services/Nv/NvMapIoctl.cs
Normal file
238
Ryujinx.Core/OsHle/Services/Nv/NvMapIoctl.cs
Normal file
|
@ -0,0 +1,238 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvMapIoctl
|
||||
{
|
||||
private NsGpuMemoryMgr Vmm;
|
||||
|
||||
private static ConcurrentDictionary<Process, IdDictionary> NvMaps;
|
||||
|
||||
private object NvMapLock;
|
||||
|
||||
private const int FlagNotFreedYet = 1;
|
||||
|
||||
private enum NvMapParam
|
||||
{
|
||||
Size = 1,
|
||||
Align = 2,
|
||||
Base = 3,
|
||||
Heap = 4,
|
||||
Kind = 5,
|
||||
Compr = 6
|
||||
}
|
||||
|
||||
public NvMapIoctl()
|
||||
{
|
||||
NvMapLock = new object();
|
||||
}
|
||||
|
||||
public int Create(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvMapCreate Args = AMemoryHelper.Read<NvMapCreate>(Context.Memory, InputPosition);
|
||||
|
||||
if (Args.Size == 0)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
Args.Handle = AddNvMap(Context, new NvMap(Args.Size));
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
public int IocFromId(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvMapFromId Args = AMemoryHelper.Read<NvMapFromId>(Context.Memory, InputPosition);
|
||||
|
||||
lock (NvMapLock)
|
||||
{
|
||||
NvMap Map = GetNvMap(Context, Args.Id);
|
||||
|
||||
if (Map == null)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
Map.IncrementRefCount();
|
||||
}
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
public int Alloc(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvMapAlloc Args = AMemoryHelper.Read<NvMapAlloc>(Context.Memory, InputPosition);
|
||||
|
||||
NvMap Map = GetNvMap(Context, Args.Handle);
|
||||
|
||||
if (Map == null)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
if ((Args.Align & (Args.Align - 1)) != 0)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
if ((uint)Args.Align < 0x1000)
|
||||
{
|
||||
Args.Align = 0x1000;
|
||||
}
|
||||
|
||||
Map.Align = Args.Align;
|
||||
|
||||
Map.Kind = (byte)Args.Kind;
|
||||
|
||||
Args.Address = Vmm.Reserve(Args.Address, (uint)Map.Size, (uint)Map.Align);
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
public int Free(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvMapFree Args = AMemoryHelper.Read<NvMapFree>(Context.Memory, InputPosition);
|
||||
|
||||
lock (NvMapLock)
|
||||
{
|
||||
NvMap Map = GetNvMap(Context, Args.Handle);
|
||||
|
||||
if (Map == null)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
long RefCount = Map.DecrementRefCount();
|
||||
|
||||
if (RefCount <= 0)
|
||||
{
|
||||
DeleteNvMap(Context, Args.Handle);
|
||||
|
||||
Args.Flags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Args.Flags = FlagNotFreedYet;
|
||||
}
|
||||
|
||||
Args.RefCount = RefCount;
|
||||
Args.Size = Map.Size;
|
||||
}
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
public int Param(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
Nv.NvMapParam Args = AMemoryHelper.Read<Nv.NvMapParam>(Context.Memory, InputPosition);
|
||||
|
||||
NvMap Map = GetNvMap(Context, Args.Handle);
|
||||
|
||||
if (Map == null)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
switch ((NvMapParam)Args.Param)
|
||||
{
|
||||
case NvMapParam.Size: Args.Result = Map.Size; break;
|
||||
case NvMapParam.Align: Args.Result = Map.Align; break;
|
||||
case NvMapParam.Heap: Args.Result = 0x40000000; break;
|
||||
case NvMapParam.Kind: Args.Result = Map.Kind; break;
|
||||
case NvMapParam.Compr: Args.Result = 0; break;
|
||||
|
||||
//Note: Base is not supported and returns an error.
|
||||
//Any other value also returns an error.
|
||||
default: return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
public int GetId(ServiceCtx Context)
|
||||
{
|
||||
long InputPosition = Context.Request.GetBufferType0x21Position();
|
||||
long OutputPosition = Context.Request.GetBufferType0x22Position();
|
||||
|
||||
NvMapGetId Args = AMemoryHelper.Read<NvMapGetId>(Context.Memory, InputPosition);
|
||||
|
||||
NvMap Map = GetNvMap(Context, Args.Handle);
|
||||
|
||||
if (Map == null)
|
||||
{
|
||||
return NvResult.InvalidInput;
|
||||
}
|
||||
|
||||
Args.Id = Args.Handle;
|
||||
|
||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||
|
||||
return NvResult.Success;
|
||||
}
|
||||
|
||||
private int AddNvMap(ServiceCtx Context, NvMap Map)
|
||||
{
|
||||
IdDictionary Maps = NvMaps.GetOrAdd(Context.Process, (Key) => new IdDictionary());
|
||||
|
||||
return Maps.Add(Map);
|
||||
}
|
||||
|
||||
private bool DeleteNvMap(ServiceCtx Context, int Handle)
|
||||
{
|
||||
if (NvMaps.TryGetValue(Context.Process, out IdDictionary Maps))
|
||||
{
|
||||
return Maps.Delete(Handle) != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public NvMap GetNvMapWithFb(ServiceCtx Context, int Handle)
|
||||
{
|
||||
if (NvMaps.TryGetValue(Context.Process, out IdDictionary Maps))
|
||||
{
|
||||
return Maps.GetData<NvMap>(Handle);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public NvMap GetNvMap(ServiceCtx Context, int Handle)
|
||||
{
|
||||
if (Handle != 0 && NvMaps.TryGetValue(Context.Process, out IdDictionary Maps))
|
||||
{
|
||||
return Maps.GetData<NvMap>(Handle);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Core/OsHle/Services/Nv/NvMapParam.cs
Normal file
9
Ryujinx.Core/OsHle/Services/Nv/NvMapParam.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
struct NvMapParam
|
||||
{
|
||||
public int Handle;
|
||||
public int Param;
|
||||
public int Result;
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/Services/Nv/NvResult.cs
Normal file
12
Ryujinx.Core/OsHle/Services/Nv/NvResult.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
static class NvResult
|
||||
{
|
||||
public const int Success = 0;
|
||||
public const int TryAgain = -11;
|
||||
public const int OutOfMemory = -12;
|
||||
public const int InvalidInput = -22;
|
||||
public const int NotSupported = -25;
|
||||
public const int TimedOut = -110;
|
||||
}
|
||||
}
|
92
Ryujinx.Core/OsHle/Services/Nv/NvSyncPt.cs
Normal file
92
Ryujinx.Core/OsHle/Services/Nv/NvSyncPt.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||
{
|
||||
class NvSyncPt
|
||||
{
|
||||
private int m_CounterMin;
|
||||
private int m_CounterMax;
|
||||
|
||||
public int CounterMin => m_CounterMin;
|
||||
public int CounterMax => m_CounterMax;
|
||||
|
||||
public bool IsEvent { get; set; }
|
||||
|
||||
private ConcurrentDictionary<EventWaitHandle, int> Waiters;
|
||||
|
||||
public NvSyncPt()
|
||||
{
|
||||
Waiters = new ConcurrentDictionary<EventWaitHandle, int>();
|
||||
}
|
||||
|
||||
public int Increment()
|
||||
{
|
||||
Interlocked.Increment(ref m_CounterMax);
|
||||
|
||||
return IncrementMin();
|
||||
}
|
||||
|
||||
public int IncrementMin()
|
||||
{
|
||||
int Value = Interlocked.Increment(ref m_CounterMin);
|
||||
|
||||
WakeUpWaiters(Value);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
public int IncrementMax()
|
||||
{
|
||||
return Interlocked.Increment(ref m_CounterMax);
|
||||
}
|
||||
|
||||
public void AddWaiter(int Threshold, EventWaitHandle WaitEvent)
|
||||
{
|
||||
if (!Waiters.TryAdd(WaitEvent, Threshold))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveWaiter(EventWaitHandle WaitEvent)
|
||||
{
|
||||
return Waiters.TryRemove(WaitEvent, out _);
|
||||
}
|
||||
|
||||
private void WakeUpWaiters(int NewValue)
|
||||
{
|
||||
foreach (KeyValuePair<EventWaitHandle, int> KV in Waiters)
|
||||
{
|
||||
if (MinCompare(NewValue, m_CounterMax, KV.Value))
|
||||
{
|
||||
KV.Key.Set();
|
||||
|
||||
Waiters.TryRemove(KV.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MinCompare(int Threshold)
|
||||
{
|
||||
return MinCompare(m_CounterMin, m_CounterMax, Threshold);
|
||||
}
|
||||
|
||||
private bool MinCompare(int Min, int Max, int Threshold)
|
||||
{
|
||||
int MinDiff = Min - Threshold;
|
||||
int MaxDiff = Max - Threshold;
|
||||
|
||||
if (IsEvent)
|
||||
{
|
||||
return MinDiff >= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (uint)MaxDiff >= (uint)MinDiff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue