diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs index 666b82792..235f29e89 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs @@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public bool IsHardwareAccelerated => false; private readonly MemoryAllocator _allocator = new(); + private byte[] _lastSegmentMap = []; + private int _lastSegmentMapMiRows; + private int _lastSegmentMapMiCols; public ISurface CreateSurface(int width, int height) { @@ -106,7 +109,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.AllocTileWorkerData(_allocator, tileCols, tileRows, maxThreads); cm.AllocContextBuffers(_allocator, output.Width, output.Height); + cm.InitContextBuffers(); + LoadSegmentMap(ref cm, ref pictureInfo); cm.SetupSegmentationDequant(); cm.SetupScaleFactors(); @@ -153,6 +158,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } + SaveSegmentMap(ref cm); cm.GetMvs(mvsOut); cm.FreeTileWorkerData(_allocator); @@ -161,6 +167,52 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return true; } + private void LoadSegmentMap(ref Vp9Common cm, ref Vp9PictureInfo pictureInfo) + { + int mapLength = cm.MiRows * cm.MiCols; + if (mapLength == 0 || cm.LastFrameSegMap.IsNull) + { + return; + } + + bool reset = + pictureInfo.IsKeyFrame || + pictureInfo.IntraOnly || + _lastSegmentMapMiRows != cm.MiRows || + _lastSegmentMapMiCols != cm.MiCols; + + if (_lastSegmentMap.Length != mapLength) + { + _lastSegmentMap = new byte[mapLength]; + } + else if (reset) + { + Array.Clear(_lastSegmentMap); + } + + _lastSegmentMapMiRows = cm.MiRows; + _lastSegmentMapMiCols = cm.MiCols; + _lastSegmentMap.CopyTo(cm.LastFrameSegMap.AsSpan()); + } + + private void SaveSegmentMap(ref Vp9Common cm) + { + int mapLength = cm.MiRows * cm.MiCols; + if (!cm.Seg.Enabled || mapLength == 0 || cm.CurrentFrameSegMap.IsNull) + { + return; + } + + if (_lastSegmentMap.Length != mapLength) + { + _lastSegmentMap = new byte[mapLength]; + } + + cm.CurrentFrameSegMap.AsSpan()[..mapLength].CopyTo(_lastSegmentMap); + _lastSegmentMapMiRows = cm.MiRows; + _lastSegmentMapMiCols = cm.MiCols; + } + public void Dispose() { _allocator.Dispose(); diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs index b4eda171f..77583eaa7 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs @@ -1088,6 +1088,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void GetMvs(Span mvs) { + if (FrameIsIntraOnly()) + { + mvs.Clear(); + return; + } + if (mvs.Length > CurFrameMvs.Length) { throw new ArgumentException( diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs index 9da64bfab..a6fb3689b 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs @@ -53,6 +53,7 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 IsKeyFrame = Flags.HasFlag(FrameFlags.IsKeyFrame), IntraOnly = Flags.HasFlag(FrameFlags.IntraOnly), UsePrevInFindMvRefs = + !Flags.HasFlag(FrameFlags.IsKeyFrame) && !Flags.HasFlag(FrameFlags.ErrorResilientMode) && !Flags.HasFlag(FrameFlags.FrameSizeChanged) && !Flags.HasFlag(FrameFlags.IntraOnly) && diff --git a/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs b/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs index a3b4e0f83..9f8f9cafb 100644 --- a/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs +++ b/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs @@ -16,10 +16,9 @@ namespace Ryujinx.Horizon.MmNv.Ipc public Result InitializeOld(Module module, uint fgmPriority, uint autoClearEvent) { bool isAutoClearEvent = autoClearEvent != 0; + uint requestId = Register(module, fgmPriority, isAutoClearEvent); - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, fgmPriority, isAutoClearEvent }); - - Register(module, fgmPriority, isAutoClearEvent); + Logger.Info?.PrintMsg(LogClass.ServiceMm, $"Initialized module={module} requestId={requestId} autoClearEvent={isAutoClearEvent}"); return Result.Success; } @@ -27,41 +26,48 @@ namespace Ryujinx.Horizon.MmNv.Ipc [CmifCommand(1)] public Result FinalizeOld(Module module) { - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module }); - lock (_sessionList) { - _sessionList.Remove(GetSessionByModule(module)); + Session session = GetSessionByModule(module); + + if (session != null) + { + _sessionList.Remove(session); + } } + Logger.Info?.PrintMsg(LogClass.ServiceMm, $"Finalized module={module}"); + return Result.Success; } [CmifCommand(2)] - public Result SetAndWaitOld(Module module, uint clockRateMin, int clockRateMax) + public Result SetAndWaitOld(Module module, uint clockRateMin, uint timeout) { - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, clockRateMin, clockRateMax }); + uint actualClockRate; lock (_sessionList) { - GetSessionByModule(module)?.SetAndWait(clockRateMin, clockRateMax); + Session session = GetSessionByModule(module) ?? RegisterSession(module, false); + ApplyClockRequest(session, clockRateMin, timeout); + actualClockRate = GetEffectiveClockRate(module); } + Logger.Trace?.PrintMsg(LogClass.ServiceMm, $"SetAndWait module={module} requested={clockRateMin} actual={actualClockRate} timeout={timeout}"); + return Result.Success; } [CmifCommand(3)] public Result GetOld(out uint clockRateActual, Module module) { - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module }); - lock (_sessionList) { - Session session = GetSessionByModule(module); - - clockRateActual = session == null ? 0 : session.ClockRateMin; + clockRateActual = GetEffectiveClockRate(module); } + Logger.Trace?.PrintMsg(LogClass.ServiceMm, $"Get module={module} actual={clockRateActual}"); + return Result.Success; } @@ -70,51 +76,76 @@ namespace Ryujinx.Horizon.MmNv.Ipc { bool isAutoClearEvent = autoClearEvent != 0; - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, fgmPriority, isAutoClearEvent }); - requestId = Register(module, fgmPriority, isAutoClearEvent); + Logger.Info?.PrintMsg(LogClass.ServiceMm, $"Initialized module={module} requestId={requestId} autoClearEvent={isAutoClearEvent}"); + return Result.Success; } [CmifCommand(5)] public Result Finalize(uint requestId) { - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId }); - lock (_sessionList) { - _sessionList.Remove(GetSessionById(requestId)); + Session session = GetSessionById(requestId); + + if (session != null) + { + _sessionList.Remove(session); + } } + Logger.Info?.PrintMsg(LogClass.ServiceMm, $"Finalized requestId={requestId}"); + return Result.Success; } [CmifCommand(6)] - public Result SetAndWait(uint requestId, uint clockRateMin, int clockRateMax) + public Result SetAndWait(uint requestId, uint clockRateMin, uint timeout) { - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId, clockRateMin, clockRateMax }); + uint actualClockRate = 0; + Module? module = null; lock (_sessionList) { - GetSessionById(requestId)?.SetAndWait(clockRateMin, clockRateMax); + Session session = GetSessionById(requestId); + + if (session != null) + { + module = session.Module; + ApplyClockRequest(session, clockRateMin, timeout); + actualClockRate = GetEffectiveClockRate(session.Module); + } } + Logger.Trace?.PrintMsg(LogClass.ServiceMm, $"SetAndWait requestId={requestId} module={module?.ToString() ?? ""} requested={clockRateMin} actual={actualClockRate} timeout={timeout}"); + return Result.Success; } [CmifCommand(7)] public Result Get(out uint clockRateActual, uint requestId) { - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId }); + Module? module = null; lock (_sessionList) { Session session = GetSessionById(requestId); - clockRateActual = session == null ? 0 : session.ClockRateMin; + if (session == null) + { + clockRateActual = 0; + } + else + { + module = session.Module; + clockRateActual = GetEffectiveClockRate(session.Module); + } } + Logger.Trace?.PrintMsg(LogClass.ServiceMm, $"Get requestId={requestId} module={module?.ToString() ?? ""} actual={clockRateActual}"); + return Result.Success; } @@ -149,12 +180,51 @@ namespace Ryujinx.Horizon.MmNv.Ipc lock (_sessionList) { // Nintendo ignores the fgm priority as the other services were deprecated. - Session session = new(_uniqueId++, module, isAutoClearEvent); - - _sessionList.Add(session); + Session session = RegisterSession(module, isAutoClearEvent); return session.Id; } } + + private Session RegisterSession(Module module, bool isAutoClearEvent) + { + Session session = new(_uniqueId++, module, isAutoClearEvent); + + _sessionList.Add(session); + + return session; + } + + private void ApplyClockRequest(Session session, uint requestedClockRate, uint timeout) + { + if (IsClockRateSentinel(requestedClockRate)) + { + session.ClearRequest(timeout); + } + else + { + session.SetAndWait(requestedClockRate, timeout); + } + } + + private uint GetEffectiveClockRate(Module module) + { + uint clockRate = 0; + + foreach (Session session in _sessionList) + { + if (session.Module == module && session.HasActiveRequest && session.RequestedClockRate > clockRate) + { + clockRate = session.RequestedClockRate; + } + } + + return clockRate; + } + + private static bool IsClockRateSentinel(uint clockRate) + { + return clockRate == 0 || unchecked((int)clockRate) < 0; + } } } diff --git a/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs b/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs index 1c2ce6552..dadc37fec 100644 --- a/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs +++ b/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Horizon.Sdk.MmNv { Result InitializeOld(Module module, uint fgmPriority, uint autoClearEvent); Result FinalizeOld(Module module); - Result SetAndWaitOld(Module module, uint clockRateMin, int clockRateMax); + Result SetAndWaitOld(Module module, uint clockRateMin, uint timeout); Result GetOld(out uint clockRateActual, Module module); Result Initialize(out uint requestId, Module module, uint fgmPriority, uint autoClearEvent); Result Finalize(uint requestId); - Result SetAndWait(uint requestId, uint clockRateMin, int clockRateMax); + Result SetAndWait(uint requestId, uint clockRateMin, uint timeout); Result Get(out uint clockRateActual, uint requestId); } } diff --git a/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs b/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs index b67a9d226..1858eed12 100644 --- a/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs +++ b/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs @@ -5,22 +5,32 @@ namespace Ryujinx.Horizon.Sdk.MmNv public Module Module { get; } public uint Id { get; } public bool IsAutoClearEvent { get; } - public uint ClockRateMin { get; private set; } - public int ClockRateMax { get; private set; } + public uint RequestedClockRate { get; private set; } + public uint LastTimeout { get; private set; } + public bool HasActiveRequest { get; private set; } public Session(uint id, Module module, bool isAutoClearEvent) { Module = module; Id = id; IsAutoClearEvent = isAutoClearEvent; - ClockRateMin = 0; - ClockRateMax = -1; + RequestedClockRate = 0; + LastTimeout = uint.MaxValue; + HasActiveRequest = false; } - public void SetAndWait(uint clockRateMin, int clockRateMax) + public void SetAndWait(uint clockRate, uint timeout) { - ClockRateMin = clockRateMin; - ClockRateMax = clockRateMax; + RequestedClockRate = clockRate; + LastTimeout = timeout; + HasActiveRequest = true; + } + + public void ClearRequest(uint timeout) + { + RequestedClockRate = 0; + LastTimeout = timeout; + HasActiveRequest = false; } } }