mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-10-21 07:21:39 +00:00
185 lines
No EOL
6 KiB
C#
185 lines
No EOL
6 KiB
C#
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Graphics.Gpu;
|
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
|
using Ryujinx.HLE.HOS.Kernel;
|
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
|
using System;
|
|
using System.Threading;
|
|
|
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
|
|
{
|
|
class NvHostEvent
|
|
{
|
|
public NvFence Fence;
|
|
public NvHostEventState State;
|
|
public KEvent Event;
|
|
public int EventHandle;
|
|
|
|
private uint _eventId;
|
|
private NvHostSyncpt _syncpointManager;
|
|
private SyncpointWaiterHandle _waiterInformation;
|
|
|
|
private NvFence _previousFailingFence;
|
|
private uint _failingCount;
|
|
|
|
public readonly object Lock = new object();
|
|
|
|
/// <summary>
|
|
/// Max failing count until waiting on CPU.
|
|
/// FIXME: This seems enough for most of the cases, reduce if needed.
|
|
/// </summary>
|
|
private const uint FailingCountMax = 2;
|
|
|
|
public NvHostEvent(NvHostSyncpt syncpointManager, uint eventId, Horizon system)
|
|
{
|
|
Fence.Id = 0;
|
|
|
|
State = NvHostEventState.Available;
|
|
|
|
Event = new KEvent(system.KernelContext);
|
|
|
|
if (KernelStatic.GetCurrentProcess().HandleTable.GenerateHandle(Event.ReadableEvent, out EventHandle) != KernelResult.Success)
|
|
{
|
|
throw new InvalidOperationException("Out of handles!");
|
|
}
|
|
|
|
_eventId = eventId;
|
|
|
|
_syncpointManager = syncpointManager;
|
|
|
|
ResetFailingState();
|
|
}
|
|
|
|
private void ResetFailingState()
|
|
{
|
|
_previousFailingFence.Id = NvFence.InvalidSyncPointId;
|
|
_previousFailingFence.Value = 0;
|
|
_failingCount = 0;
|
|
}
|
|
|
|
private void Signal()
|
|
{
|
|
lock (Lock)
|
|
{
|
|
NvHostEventState oldState = State;
|
|
|
|
State = NvHostEventState.Signaling;
|
|
|
|
if (oldState == NvHostEventState.Waiting)
|
|
{
|
|
Event.WritableEvent.Signal();
|
|
}
|
|
|
|
State = NvHostEventState.Signaled;
|
|
}
|
|
}
|
|
|
|
private void GpuSignaled(SyncpointWaiterHandle waiterInformation)
|
|
{
|
|
lock (Lock)
|
|
{
|
|
// If the signal does not match our current waiter,
|
|
// then it is from a past fence and we should just ignore it.
|
|
if (waiterInformation != null && waiterInformation != _waiterInformation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ResetFailingState();
|
|
|
|
Signal();
|
|
}
|
|
}
|
|
|
|
public void Cancel(GpuContext gpuContext)
|
|
{
|
|
lock (Lock)
|
|
{
|
|
NvHostEventState oldState = State;
|
|
|
|
State = NvHostEventState.Cancelling;
|
|
|
|
if (oldState == NvHostEventState.Waiting && _waiterInformation != null)
|
|
{
|
|
gpuContext.Synchronization.UnregisterCallback(Fence.Id, _waiterInformation);
|
|
_waiterInformation = null;
|
|
|
|
if (_previousFailingFence.Id == Fence.Id && _previousFailingFence.Value == Fence.Value)
|
|
{
|
|
_failingCount++;
|
|
}
|
|
else
|
|
{
|
|
_failingCount = 1;
|
|
|
|
_previousFailingFence = Fence;
|
|
}
|
|
}
|
|
|
|
State = NvHostEventState.Cancelled;
|
|
|
|
Event.WritableEvent.Clear();
|
|
}
|
|
}
|
|
|
|
public bool Wait(GpuContext gpuContext, NvFence fence)
|
|
{
|
|
lock (Lock)
|
|
{
|
|
// NOTE: nvservices code should always wait on the GPU side.
|
|
// If we do this, we may get an abort or undefined behaviour when the GPU processing thread is blocked for a long period (for example, during shader compilation).
|
|
// The reason for this is that the NVN code will try to wait until giving up.
|
|
// This is done by trying to wait and signal multiple times until aborting after you are past the timeout.
|
|
// As such, if it fails too many time, we enforce a wait on the CPU side indefinitely.
|
|
// This allows to keep GPU and CPU in sync when we are slow.
|
|
if (_failingCount == FailingCountMax)
|
|
{
|
|
Logger.Warning?.Print(LogClass.ServiceNv, "GPU processing thread is too slow, waiting on CPU...");
|
|
|
|
Fence.Wait(gpuContext, Timeout.InfiniteTimeSpan);
|
|
|
|
ResetFailingState();
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Fence = fence;
|
|
State = NvHostEventState.Waiting;
|
|
|
|
_waiterInformation = gpuContext.Synchronization.RegisterCallbackOnSyncpoint(Fence.Id, Fence.Value, GpuSignaled);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public string DumpState(GpuContext gpuContext)
|
|
{
|
|
string res = $"\nNvHostEvent {_eventId}:\n";
|
|
res += $"\tState: {State}\n";
|
|
|
|
if (State == NvHostEventState.Waiting)
|
|
{
|
|
res += "\tFence:\n";
|
|
res += $"\t\tId : {Fence.Id}\n";
|
|
res += $"\t\tThreshold : {Fence.Value}\n";
|
|
res += $"\t\tCurrent Value : {gpuContext.Synchronization.GetSyncpointValue(Fence.Id)}\n";
|
|
res += $"\t\tWaiter Valid : {_waiterInformation != null}\n";
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
public void CloseEvent(ServiceCtx context)
|
|
{
|
|
if (EventHandle != 0)
|
|
{
|
|
context.Process.HandleTable.CloseHandle(EventHandle);
|
|
EventHandle = 0;
|
|
}
|
|
}
|
|
}
|
|
} |