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
|
namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
{
|
{
|
||||||
class NvMap
|
class NvMap
|
||||||
|
@ -10,11 +12,28 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
public long CpuAddress;
|
public long CpuAddress;
|
||||||
public long GpuAddress;
|
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;
|
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
Add a link
Reference in a new issue