2020-05-01 21:18:42 +00:00
using Ryujinx.Common.Logging ;
2020-04-19 01:25:57 +00:00
using Ryujinx.Graphics.Gpu ;
using Ryujinx.Graphics.Gpu.Synchronization ;
using Ryujinx.HLE.HOS.Kernel.Threading ;
using Ryujinx.HLE.HOS.Services.Nv.Types ;
using System ;
2020-05-01 21:18:42 +00:00
using System.Threading ;
2020-04-19 01:25:57 +00:00
2019-09-19 00:45:11 +00:00
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
2018-05-07 18:53:23 +00:00
{
2020-04-19 01:25:57 +00:00
class NvHostEvent : IDisposable
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
2018-05-07 18:53:23 +00:00
{
2020-04-19 01:25:57 +00:00
public NvFence Fence ;
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
2018-05-07 18:53:23 +00:00
public NvHostEventState State ;
2020-04-19 01:25:57 +00:00
public KEvent Event ;
private uint _eventId ;
private NvHostSyncpt _syncpointManager ;
private SyncpointWaiterHandle _waiterInformation ;
2020-05-01 21:18:42 +00:00
private NvFence _previousFailingFence ;
private uint _failingCount ;
2020-05-02 20:47:06 +00:00
public object Lock = new object ( ) ;
2020-05-01 21:18:42 +00:00
/// <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 ;
2020-04-19 01:25:57 +00:00
public NvHostEvent ( NvHostSyncpt syncpointManager , uint eventId , Horizon system )
{
Fence . Id = 0 ;
State = NvHostEventState . Available ;
2020-05-04 03:41:29 +00:00
Event = new KEvent ( system . KernelContext ) ;
2020-04-19 01:25:57 +00:00
_eventId = eventId ;
_syncpointManager = syncpointManager ;
2020-05-01 21:18:42 +00:00
ResetFailingState ( ) ;
}
private void ResetFailingState ( )
{
_previousFailingFence . Id = NvFence . InvalidSyncPointId ;
_previousFailingFence . Value = 0 ;
_failingCount = 0 ;
2020-04-19 01:25:57 +00:00
}
private void Signal ( )
{
2020-05-02 20:47:06 +00:00
lock ( Lock )
{
NvHostEventState oldState = State ;
2020-04-19 01:25:57 +00:00
2020-05-02 20:47:06 +00:00
State = NvHostEventState . Signaling ;
2020-04-19 01:25:57 +00:00
2020-05-02 20:47:06 +00:00
if ( oldState = = NvHostEventState . Waiting )
{
Event . WritableEvent . Signal ( ) ;
}
2020-04-19 01:25:57 +00:00
2020-05-02 20:47:06 +00:00
State = NvHostEventState . Signaled ;
}
2020-04-19 01:25:57 +00:00
}
2021-10-19 20:25:32 +00:00
private void GpuSignaled ( SyncpointWaiterHandle waiterInformation )
2020-04-19 01:25:57 +00:00
{
2020-05-02 20:47:06 +00:00
lock ( Lock )
{
2021-10-19 20:25:32 +00:00
// 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 ;
}
2020-05-02 20:47:06 +00:00
ResetFailingState ( ) ;
2020-05-01 21:18:42 +00:00
2020-05-02 20:47:06 +00:00
Signal ( ) ;
}
2020-04-19 01:25:57 +00:00
}
public void Cancel ( GpuContext gpuContext )
{
2020-05-02 20:47:06 +00:00
lock ( Lock )
2020-04-19 01:25:57 +00:00
{
2021-10-19 20:25:32 +00:00
NvHostEventState oldState = State ;
State = NvHostEventState . Cancelling ;
if ( oldState = = NvHostEventState . Waiting & & _waiterInformation ! = null )
2020-05-01 21:18:42 +00:00
{
2020-05-02 20:47:06 +00:00
gpuContext . Synchronization . UnregisterCallback ( Fence . Id , _waiterInformation ) ;
2021-10-19 20:25:32 +00:00
_waiterInformation = null ;
2020-05-02 20:47:06 +00:00
if ( _previousFailingFence . Id = = Fence . Id & & _previousFailingFence . Value = = Fence . Value )
{
_failingCount + + ;
}
else
{
_failingCount = 1 ;
2020-05-01 21:18:42 +00:00
2020-05-02 20:47:06 +00:00
_previousFailingFence = Fence ;
}
2020-05-01 21:18:42 +00:00
}
2021-10-19 20:25:32 +00:00
State = NvHostEventState . Cancelled ;
2020-05-02 20:47:06 +00:00
Event . WritableEvent . Clear ( ) ;
2020-04-19 01:25:57 +00:00
}
}
2020-05-01 21:18:42 +00:00
public bool Wait ( GpuContext gpuContext , NvFence fence )
2020-04-19 01:25:57 +00:00
{
2020-05-02 20:47:06 +00:00
lock ( Lock )
2020-05-01 21:18:42 +00:00
{
2020-05-02 20:47:06 +00:00
// 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 )
{
2020-08-03 23:32:53 +00:00
Logger . Warning ? . Print ( LogClass . ServiceNv , "GPU processing thread is too slow, waiting on CPU..." ) ;
2020-05-01 21:18:42 +00:00
2021-11-01 22:10:02 +00:00
Fence . Wait ( gpuContext , Timeout . InfiniteTimeSpan ) ;
2020-05-01 21:18:42 +00:00
2021-10-19 20:25:32 +00:00
ResetFailingState ( ) ;
2020-05-01 21:18:42 +00:00
2021-11-01 22:10:02 +00:00
return false ;
2020-05-02 20:47:06 +00:00
}
else
{
2021-10-19 20:25:32 +00:00
Fence = fence ;
State = NvHostEventState . Waiting ;
2020-05-02 20:47:06 +00:00
_waiterInformation = gpuContext . Synchronization . RegisterCallbackOnSyncpoint ( Fence . Id , Fence . Value , GpuSignaled ) ;
2020-05-01 21:18:42 +00:00
2020-05-02 20:47:06 +00:00
return true ;
}
2020-05-01 21:18:42 +00:00
}
2020-04-19 01:25:57 +00:00
}
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 Dispose ( )
{
Event . ReadableEvent . DecrementReferenceCount ( ) ;
Event . WritableEvent . DecrementReferenceCount ( ) ;
}
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
2018-05-07 18:53:23 +00:00
}
}