Compare commits

..

3 Commits

Author SHA1 Message Date
4df22eb867 Fix missing data for new copy dependency textures with mismatching size (#6161) 2024-01-22 17:42:26 -03:00
f241f88558 Add a separate device memory manager (#6153)
* Add a separate device memory manager

* Still need this

* Device writes are always tracked

* Device writes are always tracked (2)

* Rename more instances of gmm to mm
2024-01-22 17:14:46 -03:00
90455a05e6 Input: Improve controller identification (#6029)
* Input: Improve controller identification

Controllers were identified before by a combination of their _global_ index in the list of controllers and their GUID. The problem is, disconnecting and reconnecting a controller can change its global index; the controller can appear at the end. This would give it another ID, and the controller would need to be reconfigured.

This happened to me a lot with a switch pro controller and a USB game controller, it was essentially random which appeared first. Now, it consistently detects them.

This PR changes the controller identification to be a combination of an index of controllers with the same GUID (generally 0), and its GUID. It also reworks managing the list of controllers to properly consider instance IDs.

This also changes the NpadManager to attempt to reuse old controllers when refreshing input configuration, which can prevent input from going dead for seconds whenever a controller connects or disconnects (and the switch pro controller just entirely dying).

Testing with different controller types, OS and Avalonia is welcome. Remember that the target is connecting a ton of controllers, and pulling/reconnecting them.

* Remove double empty line
2024-01-22 17:02:44 -03:00
36 changed files with 651 additions and 192 deletions

View File

@ -0,0 +1,395 @@
using Ryujinx.Memory;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Device
{
/// <summary>
/// Device memory manager.
/// </summary>
public class DeviceMemoryManager : IWritableBlock
{
private const int PtLvl0Bits = 10;
private const int PtLvl1Bits = 10;
public const int PtPageBits = 12;
private const ulong PtLvl0Size = 1UL << PtLvl0Bits;
private const ulong PtLvl1Size = 1UL << PtLvl1Bits;
public const ulong PageSize = 1UL << PtPageBits;
private const ulong PtLvl0Mask = PtLvl0Size - 1;
private const ulong PtLvl1Mask = PtLvl1Size - 1;
public const ulong PageMask = PageSize - 1;
private const int PtLvl0Bit = PtPageBits + PtLvl1Bits;
private const int PtLvl1Bit = PtPageBits;
private const int AddressSpaceBits = PtPageBits + PtLvl1Bits + PtLvl0Bits;
public const ulong PteUnmapped = ulong.MaxValue;
private readonly ulong[][] _pageTable;
private readonly IVirtualMemoryManager _physical;
/// <summary>
/// Creates a new instance of the GPU memory manager.
/// </summary>
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
public DeviceMemoryManager(IVirtualMemoryManager physicalMemory)
{
_physical = physicalMemory;
_pageTable = new ulong[PtLvl0Size][];
}
/// <summary>
/// Reads data from GPU mapped memory.
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
/// <param name="va">GPU virtual address where the data is located</param>
/// <returns>The data at the specified memory location</returns>
public T Read<T>(ulong va) where T : unmanaged
{
int size = Unsafe.SizeOf<T>();
if (IsContiguous(va, size))
{
return _physical.Read<T>(Translate(va));
}
else
{
Span<byte> data = new byte[size];
ReadImpl(va, data);
return MemoryMarshal.Cast<byte, T>(data)[0];
}
}
/// <summary>
/// Gets a read-only span of data from GPU mapped memory.
/// </summary>
/// <param name="va">GPU virtual address where the data is located</param>
/// <param name="size">Size of the data</param>
/// <returns>The span of the data at the specified memory location</returns>
public ReadOnlySpan<byte> GetSpan(ulong va, int size)
{
if (IsContiguous(va, size))
{
return _physical.GetSpan(Translate(va), size);
}
else
{
Span<byte> data = new byte[size];
ReadImpl(va, data);
return data;
}
}
/// <summary>
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
/// </summary>
/// <param name="va">GPU virtual address of the data</param>
/// <param name="data">Span to write the read data into</param>
private void ReadImpl(ulong va, Span<byte> data)
{
if (data.Length == 0)
{
return;
}
int offset = 0, size;
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
if (pa != PteUnmapped && _physical.IsMapped(pa))
{
_physical.GetSpan(pa, size).CopyTo(data[..size]);
}
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
if (pa != PteUnmapped && _physical.IsMapped(pa))
{
_physical.GetSpan(pa, size).CopyTo(data.Slice(offset, size));
}
}
}
/// <summary>
/// Gets a writable region from GPU mapped memory.
/// </summary>
/// <param name="va">Start address of the range</param>
/// <param name="size">Size in bytes to be range</param>
/// <returns>A writable region with the data at the specified memory location</returns>
public WritableRegion GetWritableRegion(ulong va, int size)
{
if (IsContiguous(va, size))
{
return _physical.GetWritableRegion(Translate(va), size, tracked: true);
}
else
{
Memory<byte> memory = new byte[size];
GetSpan(va, size).CopyTo(memory.Span);
return new WritableRegion(this, va, memory, tracked: true);
}
}
/// <summary>
/// Writes data to GPU mapped memory.
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
/// <param name="va">GPU virtual address to write the value into</param>
/// <param name="value">The value to be written</param>
public void Write<T>(ulong va, T value) where T : unmanaged
{
Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
}
/// <summary>
/// Writes data to GPU mapped memory.
/// </summary>
/// <param name="va">GPU virtual address to write the data into</param>
/// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data)
{
if (IsContiguous(va, data.Length))
{
_physical.Write(Translate(va), data);
}
else
{
int offset = 0, size;
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
if (pa != PteUnmapped && _physical.IsMapped(pa))
{
_physical.Write(pa, data[..size]);
}
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
if (pa != PteUnmapped && _physical.IsMapped(pa))
{
_physical.Write(pa, data.Slice(offset, size));
}
}
}
}
/// <summary>
/// Writes data to GPU mapped memory without write tracking.
/// </summary>
/// <param name="va">GPU virtual address to write the data into</param>
/// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
throw new NotSupportedException();
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
public void Map(ulong pa, ulong va, ulong size)
{
lock (_pageTable)
{
for (ulong offset = 0; offset < size; offset += PageSize)
{
SetPte(va + offset, PackPte(pa + offset));
}
}
}
/// <summary>
/// Unmaps a given range of pages at the specified GPU virtual memory region.
/// </summary>
/// <param name="va">GPU virtual address to unmap</param>
/// <param name="size">Size in bytes of the region being unmapped</param>
public void Unmap(ulong va, ulong size)
{
lock (_pageTable)
{
for (ulong offset = 0; offset < size; offset += PageSize)
{
SetPte(va + offset, PteUnmapped);
}
}
}
/// <summary>
/// Checks if a region of GPU mapped memory is contiguous.
/// </summary>
/// <param name="va">GPU virtual address of the region</param>
/// <param name="size">Size of the region</param>
/// <returns>True if the region is contiguous, false otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsContiguous(ulong va, int size)
{
if (!ValidateAddress(va) || GetPte(va) == PteUnmapped)
{
return false;
}
ulong endVa = (va + (ulong)size + PageMask) & ~PageMask;
va &= ~PageMask;
int pages = (int)((endVa - va) / PageSize);
for (int page = 0; page < pages - 1; page++)
{
if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped)
{
return false;
}
if (Translate(va) + PageSize != Translate(va + PageSize))
{
return false;
}
va += PageSize;
}
return true;
}
/// <summary>
/// Validates a GPU virtual address.
/// </summary>
/// <param name="va">Address to validate</param>
/// <returns>True if the address is valid, false otherwise</returns>
private static bool ValidateAddress(ulong va)
{
return va < (1UL << AddressSpaceBits);
}
/// <summary>
/// Checks if a given page is mapped.
/// </summary>
/// <param name="va">GPU virtual address of the page to check</param>
/// <returns>True if the page is mapped, false otherwise</returns>
public bool IsMapped(ulong va)
{
return Translate(va) != PteUnmapped;
}
/// <summary>
/// Translates a GPU virtual address to a CPU virtual address.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <returns>CPU virtual address, or <see cref="PteUnmapped"/> if unmapped</returns>
public ulong Translate(ulong va)
{
if (!ValidateAddress(va))
{
return PteUnmapped;
}
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return PteUnmapped;
}
return UnpackPaFromPte(pte) + (va & PageMask);
}
/// <summary>
/// Gets the Page Table entry for a given GPU virtual address.
/// </summary>
/// <param name="va">GPU virtual address</param>
/// <returns>Page table entry (CPU virtual address)</returns>
private ulong GetPte(ulong va)
{
ulong l0 = (va >> PtLvl0Bit) & PtLvl0Mask;
ulong l1 = (va >> PtLvl1Bit) & PtLvl1Mask;
if (_pageTable[l0] == null)
{
return PteUnmapped;
}
return _pageTable[l0][l1];
}
/// <summary>
/// Sets a Page Table entry at a given GPU virtual address.
/// </summary>
/// <param name="va">GPU virtual address</param>
/// <param name="pte">Page table entry (CPU virtual address)</param>
private void SetPte(ulong va, ulong pte)
{
ulong l0 = (va >> PtLvl0Bit) & PtLvl0Mask;
ulong l1 = (va >> PtLvl1Bit) & PtLvl1Mask;
if (_pageTable[l0] == null)
{
_pageTable[l0] = new ulong[PtLvl1Size];
for (ulong index = 0; index < PtLvl1Size; index++)
{
_pageTable[l0][index] = PteUnmapped;
}
}
_pageTable[l0][l1] = pte;
}
/// <summary>
/// Creates a page table entry from a physical address and kind.
/// </summary>
/// <param name="pa">Physical address</param>
/// <returns>Page table entry</returns>
private static ulong PackPte(ulong pa)
{
return pa;
}
/// <summary>
/// Unpacks physical address from a page table entry.
/// </summary>
/// <param name="pte">Page table entry</param>
/// <returns>Physical address</returns>
private static ulong UnpackPaFromPte(ulong pte)
{
return pte;
}
}
}

View File

@ -0,0 +1,39 @@
using Ryujinx.Common.Logging;
using System;
using System.Threading;
namespace Ryujinx.Graphics.Device
{
/// <summary>
/// Synchronization manager interface.
/// </summary>
public interface ISynchronizationManager
{
/// <summary>
/// Increment the value of a syncpoint with a given id.
/// </summary>
/// <param name="id">The id of the syncpoint</param>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
/// <returns>The incremented value of the syncpoint</returns>
uint IncrementSyncpoint(uint id);
/// <summary>
/// Get the value of a syncpoint with a given id.
/// </summary>
/// <param name="id">The id of the syncpoint</param>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
/// <returns>The value of the syncpoint</returns>
uint GetSyncpointValue(uint id);
/// <summary>
/// Wait on a syncpoint with a given id at a target threshold.
/// The callback will be called once the threshold is reached and will automatically be unregistered.
/// </summary>
/// <param name="id">The id of the syncpoint</param>
/// <param name="threshold">The target threshold</param>
/// <param name="timeout">The timeout</param>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
/// <returns>True if timed out</returns>
bool WaitOnSyncpoint(uint id, uint threshold, TimeSpan timeout);
}
}

View File

@ -4,4 +4,8 @@
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory;
@ -163,6 +164,22 @@ namespace Ryujinx.Graphics.Gpu
return new MemoryManager(physicalMemory);
}
/// <summary>
/// Creates a new device memory manager.
/// </summary>
/// <param name="pid">ID of the process that owns the memory manager</param>
/// <returns>The memory manager</returns>
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
public DeviceMemoryManager CreateDeviceMemoryManager(ulong pid)
{
if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
{
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
}
return physicalMemory.CreateDeviceMemoryManager();
}
/// <summary>
/// Registers virtual memory used by a process for GPU memory access, caching and read/write tracking.
/// </summary>

View File

@ -790,8 +790,12 @@ namespace Ryujinx.Graphics.Gpu.Image
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
// If the new texture is larger than the existing one, we need to fill the remaining space with CPU data,
// otherwise we only need the data that is copied from the existing texture, without loading the CPU data.
bool updateNewTexture = texture.Width > overlap.Width || texture.Height > overlap.Height;
texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>());
texture.InitializeData(false, false);
texture.InitializeData(false, updateNewTexture);
overlap.SynchronizeMemory();
overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);

View File

@ -329,49 +329,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
/// <summary>
/// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any.
/// </summary>
/// <param name="va">GPU virtual address to write the data into</param>
/// <param name="data">The data to be written</param>
public void WriteMapped(ulong va, ReadOnlySpan<byte> data)
{
if (IsContiguous(va, data.Length))
{
Physical.Write(Translate(va), data);
}
else
{
int offset = 0, size;
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
if (pa != PteUnmapped && Physical.IsMapped(pa))
{
Physical.Write(pa, data[..size]);
}
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
if (pa != PteUnmapped && Physical.IsMapped(pa))
{
Physical.Write(pa, data.Slice(offset, size));
}
}
}
}
/// <summary>
/// Runs remap actions that are added to an unmap event.
/// These must run after the mapping completes.

View File

@ -1,4 +1,5 @@
using Ryujinx.Cpu;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Memory;
@ -82,6 +83,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
/// <summary>
/// Creates a new device memory manager.
/// </summary>
/// <returns>The memory manager</returns>
public DeviceMemoryManager CreateDeviceMemoryManager()
{
return new DeviceMemoryManager(_cpuMemory);
}
/// <summary>
/// Gets a host pointer for a given range of application memory.
/// If the memory region is not a single contiguous block, this method returns 0.

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Device;
using System;
using System.Threading;
@ -7,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
/// <summary>
/// GPU synchronization manager.
/// </summary>
public class SynchronizationManager
public class SynchronizationManager : ISynchronizationManager
{
/// <summary>
/// The maximum number of syncpoints supported by the GM20B.
@ -29,12 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
}
}
/// <summary>
/// Increment the value of a syncpoint with a given id.
/// </summary>
/// <param name="id">The id of the syncpoint</param>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
/// <returns>The incremented value of the syncpoint</returns>
/// <inheritdoc/>
public uint IncrementSyncpoint(uint id)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
@ -42,12 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
return _syncpoints[id].Increment();
}
/// <summary>
/// Get the value of a syncpoint with a given id.
/// </summary>
/// <param name="id">The id of the syncpoint</param>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
/// <returns>The value of the syncpoint</returns>
/// <inheritdoc/>
public uint GetSyncpointValue(uint id)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
@ -84,15 +75,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
_syncpoints[id].UnregisterCallback(waiterInformation);
}
/// <summary>
/// Wait on a syncpoint with a given id at a target threshold.
/// The callback will be called once the threshold is reached and will automatically be unregistered.
/// </summary>
/// <param name="id">The id of the syncpoint</param>
/// <param name="threshold">The target threshold</param>
/// <param name="timeout">The timeout</param>
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
/// <returns>True if timed out</returns>
/// <inheritdoc/>
public bool WaitOnSyncpoint(uint id, uint threshold, TimeSpan timeout)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);

View File

@ -1,5 +1,4 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Synchronization;
using System.Collections.Generic;
using System.Threading;
@ -7,10 +6,10 @@ namespace Ryujinx.Graphics.Host1x
{
public class Host1xClass : IDeviceState
{
private readonly SynchronizationManager _syncMgr;
private readonly ISynchronizationManager _syncMgr;
private readonly DeviceState<Host1xClassRegisters> _state;
public Host1xClass(SynchronizationManager syncMgr)
public Host1xClass(ISynchronizationManager syncMgr)
{
_syncMgr = syncMgr;
_state = new DeviceState<Host1xClassRegisters>(new Dictionary<string, RwCallback>

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Synchronization;
using System;
using System.Numerics;
@ -35,7 +34,7 @@ namespace Ryujinx.Graphics.Host1x
private int _mask;
private bool _incrementing;
public Host1xDevice(SynchronizationManager syncMgr)
public Host1xDevice(ISynchronizationManager syncMgr)
{
_syncptIncrMgr = new SyncptIncrManager(syncMgr);
_commandQueue = new AsyncWorkQueue<Command>(Process, "Ryujinx.Host1xProcessor");

View File

@ -6,7 +6,6 @@
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
</ItemGroup>
</Project>

View File

@ -1,11 +1,11 @@
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Graphics.Device;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Host1x
{
class SyncptIncrManager
{
private readonly SynchronizationManager _syncMgr;
private readonly ISynchronizationManager _syncMgr;
private readonly struct SyncptIncr
{
@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Host1x
private uint _currentId;
public SyncptIncrManager(SynchronizationManager syncMgr)
public SyncptIncrManager(ISynchronizationManager syncMgr)
{
_syncMgr = syncMgr;
}

View File

@ -12,10 +12,10 @@ namespace Ryujinx.Graphics.Nvdec
public static void Decode(NvdecDecoderContext context, ResourceManager rm, ref NvdecRegisters state)
{
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
PictureInfo pictureInfo = rm.MemoryManager.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
H264PictureInfo info = pictureInfo.Convert();
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize);
ReadOnlySpan<byte> bitstream = rm.MemoryManager.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize);
int width = (int)pictureInfo.PicWidthInMbs * MbSizeInPixels;
int height = (int)pictureInfo.PicHeightInMbs * MbSizeInPixels;
@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Nvdec
if (outputSurface.Field == FrameField.Progressive)
{
SurfaceWriter.Write(
rm.Gmm,
rm.MemoryManager,
outputSurface,
lumaOffset + pictureInfo.LumaFrameOffset,
chromaOffset + pictureInfo.ChromaFrameOffset);
@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Nvdec
else
{
SurfaceWriter.WriteInterlaced(
rm.Gmm,
rm.MemoryManager,
outputSurface,
lumaOffset + pictureInfo.LumaTopFieldOffset,
chromaOffset + pictureInfo.ChromaTopFieldOffset,

View File

@ -1,4 +1,4 @@
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Video;
using System;
using System.Diagnostics;
@ -27,11 +27,11 @@ namespace Ryujinx.Graphics.Nvdec.Image
private readonly CacheItem[] _pool = new CacheItem[MaxItems];
private readonly MemoryManager _gmm;
private readonly DeviceMemoryManager _mm;
public SurfaceCache(MemoryManager gmm)
public SurfaceCache(DeviceMemoryManager mm)
{
_gmm = gmm;
_mm = mm;
}
public ISurface Get(IDecoder decoder, uint lumaOffset, uint chromaOffset, int width, int height)
@ -77,7 +77,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
if ((lumaOffset | chromaOffset) != 0)
{
SurfaceReader.Read(_gmm, surface, lumaOffset, chromaOffset);
SurfaceReader.Read(_mm, surface, lumaOffset, chromaOffset);
}
MoveToFront(i);
@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
if ((lumaOffset | chromaOffset) != 0)
{
SurfaceReader.Read(_gmm, surface, lumaOffset, chromaOffset);
SurfaceReader.Read(_mm, surface, lumaOffset, chromaOffset);
}
MoveToFront(MaxItems - 1);

View File

@ -1,5 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Texture;
using Ryujinx.Graphics.Video;
using System;
@ -11,13 +11,13 @@ namespace Ryujinx.Graphics.Nvdec.Image
{
static class SurfaceReader
{
public static void Read(MemoryManager gmm, ISurface surface, uint lumaOffset, uint chromaOffset)
public static void Read(DeviceMemoryManager mm, ISurface surface, uint lumaOffset, uint chromaOffset)
{
int width = surface.Width;
int height = surface.Height;
int stride = surface.Stride;
ReadOnlySpan<byte> luma = gmm.DeviceGetSpan(lumaOffset, GetBlockLinearSize(width, height, 1));
ReadOnlySpan<byte> luma = mm.DeviceGetSpan(lumaOffset, GetBlockLinearSize(width, height, 1));
ReadLuma(surface.YPlane.AsSpan(), luma, stride, width, height);
@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
int uvHeight = surface.UvHeight;
int uvStride = surface.UvStride;
ReadOnlySpan<byte> chroma = gmm.DeviceGetSpan(chromaOffset, GetBlockLinearSize(uvWidth, uvHeight, 2));
ReadOnlySpan<byte> chroma = mm.DeviceGetSpan(chromaOffset, GetBlockLinearSize(uvWidth, uvHeight, 2));
ReadChroma(surface.UPlane.AsSpan(), surface.VPlane.AsSpan(), chroma, uvStride, uvWidth, uvHeight);
}

View File

@ -1,5 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Texture;
using Ryujinx.Graphics.Video;
using System;
@ -12,11 +12,11 @@ namespace Ryujinx.Graphics.Nvdec.Image
{
static class SurfaceWriter
{
public static void Write(MemoryManager gmm, ISurface surface, uint lumaOffset, uint chromaOffset)
public static void Write(DeviceMemoryManager mm, ISurface surface, uint lumaOffset, uint chromaOffset)
{
int lumaSize = GetBlockLinearSize(surface.Width, surface.Height, 1);
using var luma = gmm.GetWritableRegion(ExtendOffset(lumaOffset), lumaSize);
using var luma = mm.GetWritableRegion(ExtendOffset(lumaOffset), lumaSize);
WriteLuma(
luma.Memory.Span,
@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
int chromaSize = GetBlockLinearSize(surface.UvWidth, surface.UvHeight, 2);
using var chroma = gmm.GetWritableRegion(ExtendOffset(chromaOffset), chromaSize);
using var chroma = mm.GetWritableRegion(ExtendOffset(chromaOffset), chromaSize);
WriteChroma(
chroma.Memory.Span,
@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
}
public static void WriteInterlaced(
MemoryManager gmm,
DeviceMemoryManager mm,
ISurface surface,
uint lumaTopOffset,
uint chromaTopOffset,
@ -48,8 +48,8 @@ namespace Ryujinx.Graphics.Nvdec.Image
{
int lumaSize = GetBlockLinearSize(surface.Width, surface.Height / 2, 1);
using var lumaTop = gmm.GetWritableRegion(ExtendOffset(lumaTopOffset), lumaSize);
using var lumaBottom = gmm.GetWritableRegion(ExtendOffset(lumaBottomOffset), lumaSize);
using var lumaTop = mm.GetWritableRegion(ExtendOffset(lumaTopOffset), lumaSize);
using var lumaBottom = mm.GetWritableRegion(ExtendOffset(lumaBottomOffset), lumaSize);
WriteLuma(
lumaTop.Memory.Span,
@ -67,8 +67,8 @@ namespace Ryujinx.Graphics.Nvdec.Image
int chromaSize = GetBlockLinearSize(surface.UvWidth, surface.UvHeight / 2, 2);
using var chromaTop = gmm.GetWritableRegion(ExtendOffset(chromaTopOffset), chromaSize);
using var chromaBottom = gmm.GetWritableRegion(ExtendOffset(chromaBottomOffset), chromaSize);
using var chromaTop = mm.GetWritableRegion(ExtendOffset(chromaTopOffset), chromaSize);
using var chromaBottom = mm.GetWritableRegion(ExtendOffset(chromaBottomOffset), chromaSize);
WriteChroma(
chromaTop.Memory.Span,

View File

@ -1,23 +1,23 @@
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using System;
namespace Ryujinx.Graphics.Nvdec
{
static class MemoryExtensions
{
public static T DeviceRead<T>(this MemoryManager gmm, uint offset) where T : unmanaged
public static T DeviceRead<T>(this DeviceMemoryManager gmm, uint offset) where T : unmanaged
{
return gmm.Read<T>((ulong)offset << 8);
return gmm.Read<T>(ExtendOffset(offset));
}
public static ReadOnlySpan<byte> DeviceGetSpan(this MemoryManager gmm, uint offset, int size)
public static ReadOnlySpan<byte> DeviceGetSpan(this DeviceMemoryManager gmm, uint offset, int size)
{
return gmm.GetSpan((ulong)offset << 8, size);
return gmm.GetSpan(ExtendOffset(offset), size);
}
public static void DeviceWrite(this MemoryManager gmm, uint offset, ReadOnlySpan<byte> data)
public static void DeviceWrite(this DeviceMemoryManager gmm, uint offset, ReadOnlySpan<byte> data)
{
gmm.Write((ulong)offset << 8, data);
gmm.Write(ExtendOffset(offset), data);
}
public static ulong ExtendOffset(uint offset)

View File

@ -1,6 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Nvdec.Image;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -17,9 +16,9 @@ namespace Ryujinx.Graphics.Nvdec
private readonly ConcurrentDictionary<long, NvdecDecoderContext> _contexts;
private NvdecDecoderContext _currentContext;
public NvdecDevice(MemoryManager gmm)
public NvdecDevice(DeviceMemoryManager mm)
{
_rm = new ResourceManager(gmm, new SurfaceCache(gmm));
_rm = new ResourceManager(mm, new SurfaceCache(mm));
_state = new DeviceState<NvdecRegisters>(new Dictionary<string, RwCallback>
{
{ nameof(NvdecRegisters.Execute), new RwCallback(Execute, null) },

View File

@ -1,16 +1,16 @@
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Nvdec.Image;
namespace Ryujinx.Graphics.Nvdec
{
readonly struct ResourceManager
{
public MemoryManager Gmm { get; }
public DeviceMemoryManager MemoryManager { get; }
public SurfaceCache Cache { get; }
public ResourceManager(MemoryManager gmm, SurfaceCache cache)
public ResourceManager(DeviceMemoryManager mm, SurfaceCache cache)
{
Gmm = gmm;
MemoryManager = mm;
Cache = cache;
}
}

View File

@ -8,7 +8,6 @@
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />

View File

@ -10,8 +10,8 @@ namespace Ryujinx.Graphics.Nvdec
{
public static void Decode(NvdecDecoderContext context, ResourceManager rm, ref NvdecRegisters state)
{
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.VLDBufferSize);
PictureInfo pictureInfo = rm.MemoryManager.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
ReadOnlySpan<byte> bitstream = rm.MemoryManager.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.VLDBufferSize);
Decoder decoder = context.GetVp8Decoder();
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Nvdec
if (decoder.Decode(ref info, outputSurface, bitstream))
{
SurfaceWriter.Write(rm.Gmm, outputSurface, lumaOffset, chromaOffset);
SurfaceWriter.Write(rm.MemoryManager, outputSurface, lumaOffset, chromaOffset);
}
rm.Cache.Put(outputSurface);

View File

@ -1,5 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Nvdec.Image;
using Ryujinx.Graphics.Nvdec.Types.Vp9;
using Ryujinx.Graphics.Nvdec.Vp9;
@ -17,8 +17,8 @@ namespace Ryujinx.Graphics.Nvdec
public unsafe static void Decode(ResourceManager rm, ref NvdecRegisters state)
{
PictureInfo pictureInfo = rm.Gmm.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
EntropyProbs entropy = rm.Gmm.DeviceRead<EntropyProbs>(state.Vp9SetProbTabBufOffset);
PictureInfo pictureInfo = rm.MemoryManager.DeviceRead<PictureInfo>(state.SetDrvPicSetupOffset);
EntropyProbs entropy = rm.MemoryManager.DeviceRead<EntropyProbs>(state.Vp9SetProbTabBufOffset);
ISurface Rent(uint lumaOffset, uint chromaOffset, FrameSize size)
{
@ -38,19 +38,19 @@ namespace Ryujinx.Graphics.Nvdec
entropy.Convert(ref info.Entropy);
ReadOnlySpan<byte> bitstream = rm.Gmm.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize);
ReadOnlySpan<byte> bitstream = rm.MemoryManager.DeviceGetSpan(state.SetInBufBaseOffset, (int)pictureInfo.BitstreamSize);
ReadOnlySpan<Vp9MvRef> mvsIn = ReadOnlySpan<Vp9MvRef>.Empty;
if (info.UsePrevInFindMvRefs)
{
mvsIn = GetMvsInput(rm.Gmm, pictureInfo.CurrentFrameSize, state.Vp9SetColMvReadBufOffset);
mvsIn = GetMvsInput(rm.MemoryManager, pictureInfo.CurrentFrameSize, state.Vp9SetColMvReadBufOffset);
}
int miCols = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Width, 8);
int miRows = BitUtils.DivRoundUp(pictureInfo.CurrentFrameSize.Height, 8);
using var mvsRegion = rm.Gmm.GetWritableRegion(ExtendOffset(state.Vp9SetColMvWriteBufOffset), miRows * miCols * 16);
using var mvsRegion = rm.MemoryManager.GetWritableRegion(ExtendOffset(state.Vp9SetColMvWriteBufOffset), miRows * miCols * 16);
Span<Vp9MvRef> mvsOut = MemoryMarshal.Cast<byte, Vp9MvRef>(mvsRegion.Memory.Span);
@ -59,10 +59,10 @@ namespace Ryujinx.Graphics.Nvdec
if (_decoder.Decode(ref info, currentSurface, bitstream, mvsIn, mvsOut))
{
SurfaceWriter.Write(rm.Gmm, currentSurface, lumaOffset, chromaOffset);
SurfaceWriter.Write(rm.MemoryManager, currentSurface, lumaOffset, chromaOffset);
}
WriteBackwardUpdates(rm.Gmm, state.Vp9SetCtxCounterBufOffset, ref info.BackwardUpdateCounts);
WriteBackwardUpdates(rm.MemoryManager, state.Vp9SetCtxCounterBufOffset, ref info.BackwardUpdateCounts);
rm.Cache.Put(lastSurface);
rm.Cache.Put(goldenSurface);
@ -70,17 +70,17 @@ namespace Ryujinx.Graphics.Nvdec
rm.Cache.Put(currentSurface);
}
private static ReadOnlySpan<Vp9MvRef> GetMvsInput(MemoryManager gmm, FrameSize size, uint offset)
private static ReadOnlySpan<Vp9MvRef> GetMvsInput(DeviceMemoryManager mm, FrameSize size, uint offset)
{
int miCols = BitUtils.DivRoundUp(size.Width, 8);
int miRows = BitUtils.DivRoundUp(size.Height, 8);
return MemoryMarshal.Cast<byte, Vp9MvRef>(gmm.DeviceGetSpan(offset, miRows * miCols * 16));
return MemoryMarshal.Cast<byte, Vp9MvRef>(mm.DeviceGetSpan(offset, miRows * miCols * 16));
}
private static void WriteBackwardUpdates(MemoryManager gmm, uint offset, ref Vp9BackwardUpdates counts)
private static void WriteBackwardUpdates(DeviceMemoryManager mm, uint offset, ref Vp9BackwardUpdates counts)
{
using var backwardUpdatesRegion = gmm.GetWritableRegion(ExtendOffset(offset), Unsafe.SizeOf<BackwardUpdates>());
using var backwardUpdatesRegion = mm.GetWritableRegion(ExtendOffset(offset), Unsafe.SizeOf<BackwardUpdates>());
ref var backwardUpdates = ref MemoryMarshal.Cast<byte, BackwardUpdates>(backwardUpdatesRegion.Memory.Span)[0];

View File

@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Vic.Image
int srcStride = GetPitch(width, bytesPerPixel);
int inSize = srcStride * height;
ReadOnlySpan<byte> src = rm.Gmm.GetSpan(ExtendOffset(offset), inSize);
ReadOnlySpan<byte> src = rm.MemoryManager.GetSpan(ExtendOffset(offset), inSize);
int outSize = dstStride * height;
int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer);
@ -481,7 +481,7 @@ namespace Ryujinx.Graphics.Vic.Image
{
int inSize = GetBlockLinearSize(width, height, bytesPerPixel, gobBlocksInY);
ReadOnlySpan<byte> src = rm.Gmm.GetSpan(ExtendOffset(offset), inSize);
ReadOnlySpan<byte> src = rm.MemoryManager.GetSpan(ExtendOffset(offset), inSize);
int outSize = dstStride * height;
int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer);

View File

@ -636,7 +636,7 @@ namespace Ryujinx.Graphics.Vic.Image
{
if (linear)
{
rm.Gmm.WriteMapped(ExtendOffset(offset), src);
rm.MemoryManager.Write(ExtendOffset(offset), src);
return;
}
@ -659,7 +659,7 @@ namespace Ryujinx.Graphics.Vic.Image
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
rm.Gmm.WriteMapped(ExtendOffset(offset), dst);
rm.MemoryManager.Write(ExtendOffset(offset), dst);
rm.BufferPool.Return(dstIndex);
}

View File

@ -1,17 +1,17 @@
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Vic.Image;
namespace Ryujinx.Graphics.Vic
{
readonly struct ResourceManager
{
public MemoryManager Gmm { get; }
public DeviceMemoryManager MemoryManager { get; }
public BufferPool<Pixel> SurfacePool { get; }
public BufferPool<byte> BufferPool { get; }
public ResourceManager(MemoryManager gmm, BufferPool<Pixel> surfacePool, BufferPool<byte> bufferPool)
public ResourceManager(DeviceMemoryManager mm, BufferPool<Pixel> surfacePool, BufferPool<byte> bufferPool)
{
Gmm = gmm;
MemoryManager = mm;
SurfacePool = surfacePool;
BufferPool = bufferPool;
}

View File

@ -8,7 +8,6 @@
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
</ItemGroup>

View File

@ -1,5 +1,4 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Vic.Image;
using Ryujinx.Graphics.Vic.Types;
using System;
@ -9,14 +8,14 @@ namespace Ryujinx.Graphics.Vic
{
public class VicDevice : IDeviceState
{
private readonly MemoryManager _gmm;
private readonly DeviceMemoryManager _mm;
private readonly ResourceManager _rm;
private readonly DeviceState<VicRegisters> _state;
public VicDevice(MemoryManager gmm)
public VicDevice(DeviceMemoryManager mm)
{
_gmm = gmm;
_rm = new ResourceManager(gmm, new BufferPool<Pixel>(), new BufferPool<byte>());
_mm = mm;
_rm = new ResourceManager(mm, new BufferPool<Pixel>(), new BufferPool<byte>());
_state = new DeviceState<VicRegisters>(new Dictionary<string, RwCallback>
{
{ nameof(VicRegisters.Execute), new RwCallback(Execute, null) },
@ -68,7 +67,7 @@ namespace Ryujinx.Graphics.Vic
private T ReadIndirect<T>(uint offset) where T : unmanaged
{
return _gmm.Read<T>((ulong)offset << 8);
return _mm.Read<T>((ulong)offset << 8);
}
}
}

View File

@ -1,4 +1,4 @@
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Host1x;
using Ryujinx.Graphics.Nvdec;
using Ryujinx.Graphics.Vic;
@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{
class Host1xContext : IDisposable
{
public MemoryManager Smmu { get; }
public DeviceMemoryManager Smmu { get; }
public NvMemoryAllocator MemoryAllocator { get; }
public Host1xDevice Host1x { get; }
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{
MemoryAllocator = new NvMemoryAllocator();
Host1x = new Host1xDevice(gpu.Synchronization);
Smmu = gpu.CreateMemoryManager(pid);
Smmu = gpu.CreateDeviceMemoryManager(pid);
var nvdec = new NvdecDevice(Smmu);
var vic = new VicDevice(Smmu);
Host1x.RegisterDevice(ClassId.Nvdec, nvdec);

View File

@ -266,7 +266,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
if (size == 0)
{
size = (uint)map.Size;
size = map.Size;
}
NvInternalResult result = NvInternalResult.Success;

View File

@ -250,12 +250,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
{
if (map.DmaMapAddress == 0)
{
ulong va = _host1xContext.MemoryAllocator.GetFreeAddress((ulong)map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
ulong va = _host1xContext.MemoryAllocator.GetFreeAddress(map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + map.Size) <= uint.MaxValue)
{
_host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size, PteKind.Pitch); // FIXME: This should not use the GMMU.
_host1xContext.MemoryAllocator.AllocateRange(va, map.Size, freeAddressStartPosition);
_host1xContext.Smmu.Map(map.Address, va, map.Size);
map.DmaMapAddress = va;
}
else

View File

@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
return NvInternalResult.InvalidInput;
}
int size = BitUtils.AlignUp(arguments.Size, (int)MemoryManager.PageSize);
uint size = BitUtils.AlignUp(arguments.Size, (uint)MemoryManager.PageSize);
arguments.Handle = CreateHandleFromMap(new NvMapHandle(size));
@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
map.Align = arguments.Align;
map.Kind = (byte)arguments.Kind;
int size = BitUtils.AlignUp(map.Size, (int)MemoryManager.PageSize);
uint size = BitUtils.AlignUp(map.Size, (uint)MemoryManager.PageSize);
ulong address = arguments.Address;
@ -191,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
switch (arguments.Param)
{
case NvMapHandleParam.Size:
arguments.Result = map.Size;
arguments.Result = (int)map.Size;
break;
case NvMapHandleParam.Align:
arguments.Result = map.Align;

View File

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
[StructLayout(LayoutKind.Sequential)]
struct NvMapCreate
{
public int Size;
public uint Size;
public int Handle;
}
}

View File

@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
public int Handle;
public int Padding;
public ulong Address;
public int Size;
public uint Size;
public int Flags;
}
}

View File

@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
public int Handle;
public int Id;
#pragma warning restore CS0649
public int Size;
public uint Size;
public int Align;
public int Kind;
public ulong Address;
@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
_dupes = 1;
}
public NvMapHandle(int size) : this()
public NvMapHandle(uint size) : this()
{
Size = size;
}

View File

@ -9,8 +9,18 @@ namespace Ryujinx.Input.SDL2
{
private readonly Dictionary<int, string> _gamepadsInstanceIdsMapping;
private readonly List<string> _gamepadsIds;
private readonly object _lock = new object();
public ReadOnlySpan<string> GamepadsIds => _gamepadsIds.ToArray();
public ReadOnlySpan<string> GamepadsIds
{
get
{
lock (_lock)
{
return _gamepadsIds.ToArray();
}
}
}
public string DriverName => "SDL2";
@ -35,28 +45,39 @@ namespace Ryujinx.Input.SDL2
}
}
private static string GenerateGamepadId(int joystickIndex)
private string GenerateGamepadId(int joystickIndex)
{
Guid guid = SDL_JoystickGetDeviceGUID(joystickIndex);
// Add a unique identifier to the start of the GUID in case of duplicates.
if (guid == Guid.Empty)
{
return null;
}
return joystickIndex + "-" + guid;
}
string id;
private static int GetJoystickIndexByGamepadId(string id)
{
string[] data = id.Split("-");
if (data.Length != 6 || !int.TryParse(data[0], out int joystickIndex))
lock (_lock)
{
return -1;
int guidIndex = 0;
id = guidIndex + "-" + guid;
while (_gamepadsIds.Contains(id))
{
id = (++guidIndex) + "-" + guid;
}
}
return joystickIndex;
return id;
}
private int GetJoystickIndexByGamepadId(string id)
{
lock (_lock)
{
return _gamepadsIds.IndexOf(id);
}
}
private void HandleJoyStickDisconnected(int joystickInstanceId)
@ -64,7 +85,11 @@ namespace Ryujinx.Input.SDL2
if (_gamepadsInstanceIdsMapping.TryGetValue(joystickInstanceId, out string id))
{
_gamepadsInstanceIdsMapping.Remove(joystickInstanceId);
_gamepadsIds.Remove(id);
lock (_lock)
{
_gamepadsIds.Remove(id);
}
OnGamepadDisconnected?.Invoke(id);
}
@ -74,6 +99,13 @@ namespace Ryujinx.Input.SDL2
{
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
{
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
{
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
// so it is rejected to avoid doubling the entries.
return;
}
string id = GenerateGamepadId(joystickDeviceId);
if (id == null)
@ -81,16 +113,12 @@ namespace Ryujinx.Input.SDL2
return;
}
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
// so it is rejected to avoid doubling the entries.
if (_gamepadsIds.Contains(id))
{
return;
}
if (_gamepadsInstanceIdsMapping.TryAdd(joystickInstanceId, id))
{
_gamepadsIds.Add(id);
lock (_lock)
{
_gamepadsIds.Add(id);
}
OnGamepadConnected?.Invoke(id);
}
@ -110,7 +138,10 @@ namespace Ryujinx.Input.SDL2
OnGamepadDisconnected?.Invoke(id);
}
_gamepadsIds.Clear();
lock (_lock)
{
_gamepadsIds.Clear();
}
SDL2Driver.Instance.Dispose();
}
@ -131,11 +162,6 @@ namespace Ryujinx.Input.SDL2
return null;
}
if (id != GenerateGamepadId(joystickIndex))
{
return null;
}
IntPtr gamepadHandle = SDL_GameControllerOpen(joystickIndex);
if (gamepadHandle == IntPtr.Zero)

View File

@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client;
using ControllerType = Ryujinx.Common.Configuration.Hid.ControllerType;
@ -69,7 +70,20 @@ namespace Ryujinx.Input.HLE
private void HandleOnGamepadDisconnected(string obj)
{
// Force input reload
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse);
lock (_lock)
{
// Forcibly disconnect any controllers with this ID.
for (int i = 0; i < _controllers.Length; i++)
{
if (_controllers[i]?.Id == obj)
{
_controllers[i]?.Dispose();
_controllers[i] = null;
}
}
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse);
}
}
private void HandleOnGamepadConnected(string id)
@ -106,31 +120,48 @@ namespace Ryujinx.Input.HLE
{
lock (_lock)
{
for (int i = 0; i < _controllers.Length; i++)
{
_controllers[i]?.Dispose();
_controllers[i] = null;
}
NpadController[] oldControllers = _controllers.ToArray();
List<InputConfig> validInputs = new();
foreach (InputConfig inputConfigEntry in inputConfig)
{
NpadController controller = new(_cemuHookClient);
NpadController controller;
int index = (int)inputConfigEntry.PlayerIndex;
if (oldControllers[index] != null)
{
// Try reuse the existing controller.
controller = oldControllers[index];
oldControllers[index] = null;
}
else
{
controller = new(_cemuHookClient);
}
bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry);
if (!isValid)
{
_controllers[index] = null;
controller.Dispose();
}
else
{
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
_controllers[index] = controller;
validInputs.Add(inputConfigEntry);
}
}
for (int i = 0; i < oldControllers.Length; i++)
{
// Disconnect any controllers that weren't reused by the new configuration.
oldControllers[i]?.Dispose();
oldControllers[i] = null;
}
_inputConfig = inputConfig;
_enableKeyboard = enableKeyboard;
_enableMouse = enableMouse;