Compare commits

...

5 Commits

Author SHA1 Message Date
riperiperi
76b53e018a GPU: Add fallback when textureGatherOffsets is not supported (#5792)
* GPU: Add fallback when textureGatherOffsets is not supported.

This PR adds a fallback for GPUs or APIs that don't support an equivalent to the method `textureGatherOffsets`, where each of the 4 gathered texels has an individual offset. This is done by reusing the existing code to handle non-const offsets for texture instructions, though it has also been corrected as there were a few implementation issues.

MoltenVK reports support for this capability, and it didn't error when we initially released the MacOS build, but that has since changed. MVK still reports support, but spirv-cross has been fixed in a way that it _attempts_ to use this capability, but the metal compiler errors since it doesn't exist.

Some other fixes:
- textureGatherOffsets emulation has been changed significantly. It now uses 4 texture sample instructions (not gather), calculates a base texel (i=0 j=0) and adds the offsets onto it before converting into a tex coord. The final result is offset into a texel center, so it shouldn't be subject to interpolation, though this isn't perfect and could have some error with floating point formats with linear sampling. It is subject to texture wrap mode as it should be, which is why texelFetch was not used.
  - Maybe gather should be used here with component `w` (i=0, j=0), though this multiplies number of texels fetched by 4... The way it was doing this before _was_ wrong_, but doing it right would avoid issues with texel center precision.
- textureGatherOffset (singular) now performs textureGather with the offset applied to the coords, rather than the slower fallback where each texel is fetched individually.

* Increment shader cache version, remove unused arg

* Use base texture size for gather coord offset.

Implicit LOD for gather is not supported.

* Use 4 texture gathers for offsets emulation

Avoids issues with interpolation at cost of performance

(not sure how bad this is)

* Address Feedback
2023-10-20 15:05:09 +02:00
gdkchan
28dd7d80af Enable copy between MS and non-MS textures with different height (#5801) 2023-10-18 04:47:22 +00:00
Ac_K
1e06b28b22 Horizon: Migrate usb and psc services (#5800)
* Horizon: Migrate Usb and Psc services

* Fix formatting

* Adresses feedback
2023-10-13 23:13:15 -03:00
sunshineinabox
e768a54f17 Replace ReaderWriterLock with ReaderWriterLockSlim (#5785)
* Replace ReaderWriterLock with ReaderWriterLockSlim

* Resolve Feedback + Correct typo

* Revert some unncessary logic
2023-10-12 18:11:15 +02:00
TSRBerry
4e2bb13080 Fix games freezing after initializing LDN 1021 times (#5787)
* Close handle for stateChangeEvent on Finalize

* Properly dispose NetworkClient before setting it to null
2023-10-09 13:47:47 +00:00
77 changed files with 867 additions and 202 deletions

View File

@@ -7,14 +7,14 @@ namespace ARMeilleure.Translation
internal class TranslatorCache<T>
{
private readonly IntervalTree<ulong, T> _tree;
private readonly ReaderWriterLock _treeLock;
private readonly ReaderWriterLockSlim _treeLock;
public int Count => _tree.Count;
public TranslatorCache()
{
_tree = new IntervalTree<ulong, T>();
_treeLock = new ReaderWriterLock();
_treeLock = new ReaderWriterLockSlim();
}
public bool TryAdd(ulong address, ulong size, T value)
@@ -24,70 +24,70 @@ namespace ARMeilleure.Translation
public bool AddOrUpdate(ulong address, ulong size, T value, Func<ulong, T, T> updateFactoryCallback)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
bool result = _tree.AddOrUpdate(address, address + size, value, updateFactoryCallback);
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
return result;
}
public T GetOrAdd(ulong address, ulong size, T value)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
value = _tree.GetOrAdd(address, address + size, value);
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
return value;
}
public bool Remove(ulong address)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
bool removed = _tree.Remove(address) != 0;
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
return removed;
}
public void Clear()
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
_tree.Clear();
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
}
public bool ContainsKey(ulong address)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
bool result = _tree.ContainsKey(address);
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return result;
}
public bool TryGetValue(ulong address, out T value)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
bool result = _tree.TryGet(address, out value);
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return result;
}
public int GetOverlaps(ulong address, ulong size, ref ulong[] overlaps)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
int count = _tree.Get(address, address + size, ref overlaps);
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return count;
}
public List<T> AsList()
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
List<T> list = _tree.AsList();
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return list;
}

View File

@@ -5,7 +5,7 @@ namespace Ryujinx.Common
{
public class ReactiveObject<T>
{
private readonly ReaderWriterLock _readerWriterLock = new();
private readonly ReaderWriterLockSlim _readerWriterLock = new();
private bool _isInitialized;
private T _value;
@@ -15,15 +15,15 @@ namespace Ryujinx.Common
{
get
{
_readerWriterLock.AcquireReaderLock(Timeout.Infinite);
_readerWriterLock.EnterReadLock();
T value = _value;
_readerWriterLock.ReleaseReaderLock();
_readerWriterLock.ExitReadLock();
return value;
}
set
{
_readerWriterLock.AcquireWriterLock(Timeout.Infinite);
_readerWriterLock.EnterWriteLock();
T oldValue = _value;
@@ -32,7 +32,7 @@ namespace Ryujinx.Common
_isInitialized = true;
_value = value;
_readerWriterLock.ReleaseWriterLock();
_readerWriterLock.ExitWriteLock();
if (!oldIsInitialized || oldValue == null || !oldValue.Equals(_value))
{

View File

@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64;
public readonly bool SupportsTextureGatherOffsets;
public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsVertexStoreAndAtomics;
public readonly bool SupportsViewportIndexVertexTessellation;
@@ -92,6 +93,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64,
bool supportsTextureGatherOffsets,
bool supportsTextureShadowLod,
bool supportsVertexStoreAndAtomics,
bool supportsViewportIndexVertexTessellation,
@@ -142,6 +144,7 @@ namespace Ryujinx.Graphics.GAL
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64;
SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;
SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation;

View File

@@ -374,6 +374,13 @@ namespace Ryujinx.Graphics.Gpu.Image
return stride == rhs.Stride ? TextureViewCompatibility.CopyOnly : TextureViewCompatibility.LayoutIncompatible;
}
else if (lhs.Target.IsMultisample() != rhs.Target.IsMultisample() && alignedWidthMatches && lhsAlignedSize.Height == rhsAlignedSize.Height)
{
// Copy between multisample and non-multisample textures with mismatching size is allowed,
// as long aligned size matches.
return TextureViewCompatibility.CopyOnly;
}
else
{
return TextureViewCompatibility.LayoutIncompatible;

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 5767;
private const uint CodeGenVersion = 5791;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -186,6 +186,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
public bool QueryHostSupportsTransformFeedback() => _context.Capabilities.SupportsTransformFeedback;

View File

@@ -163,6 +163,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true,
supportsTextureGatherOffsets: true,
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
supportsVertexStoreAndAtomics: true,
supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,

View File

@@ -339,6 +339,15 @@ namespace Ryujinx.Graphics.Shader
return true;
}
/// <summary>
/// Queries host GPU texture gather with multiple offsets support.
/// </summary>
/// <returns>True if the GPU and driver supports texture gather offsets, false otherwise</returns>
bool QueryHostSupportsTextureGatherOffsets()
{
return true;
}
/// <summary>
/// Queries host GPU texture shadow LOD support.
/// </summary>

View File

@@ -303,7 +303,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
bool hasInvalidOffset = (hasOffset || hasOffsets) && !gpuAccessor.QueryHostSupportsNonConstantTextureOffset();
bool needsOffsetsEmulation = hasOffsets && !gpuAccessor.QueryHostSupportsTextureGatherOffsets();
bool hasInvalidOffset = needsOffsetsEmulation || ((hasOffset || hasOffsets) && !gpuAccessor.QueryHostSupportsNonConstantTextureOffset());
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
@@ -402,11 +404,14 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
offsets[index] = offset;
}
hasInvalidOffset &= !areAllOffsetsConstant;
if (!hasInvalidOffset)
if (!needsOffsetsEmulation)
{
return node;
hasInvalidOffset &= !areAllOffsetsConstant;
if (!hasInvalidOffset)
{
return node;
}
}
if (hasLodBias)
@@ -434,13 +439,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
LinkedListNode<INode> oldNode = node;
if (isGather && !isShadow)
if (isGather && !isShadow && hasOffsets)
{
Operand[] newSources = new Operand[sources.Length];
sources.CopyTo(newSources, 0);
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
Operand[] texSizes = InsertTextureBaseSize(node, texOp, bindlessHandle, coordsCount);
int destIndex = 0;
@@ -455,7 +460,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
{
Operand offset = Local();
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
Operand intOffset = offsets[index + compIndex * coordsCount];
node.List.AddBefore(node, new Operation(
Instruction.FP32 | Instruction.Divide,
@@ -478,7 +483,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
texOp.Format,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.Binding,
1,
1 << 3, // W component: i=0, j=0
new[] { dests[destIndex++] },
newSources);
@@ -502,7 +507,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
}
else
{
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
Operand[] texSizes = isGather
? InsertTextureBaseSize(node, texOp, bindlessHandle, coordsCount)
: InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
for (int index = 0; index < coordsCount; index++)
{
@@ -549,6 +556,43 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
return node;
}
private static Operand[] InsertTextureBaseSize(
LinkedListNode<INode> node,
TextureOperation texOp,
Operand bindlessHandle,
int coordsCount)
{
Operand[] texSizes = new Operand[coordsCount];
for (int index = 0; index < coordsCount; index++)
{
texSizes[index] = Local();
Operand[] texSizeSources;
if (bindlessHandle != null)
{
texSizeSources = new Operand[] { bindlessHandle, Const(0) };
}
else
{
texSizeSources = new Operand[] { Const(0) };
}
node.List.AddBefore(node, new TextureOperation(
Instruction.TextureQuerySize,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.Binding,
index,
new[] { texSizes[index] },
texSizeSources));
}
return texSizes;
}
private static Operand[] InsertTextureLod(
LinkedListNode<INode> node,
TextureOperation texOp,

View File

@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Vulkan
private int _flushTemp;
private int _lastFlushWrite = -1;
private readonly ReaderWriterLock _flushLock;
private readonly ReaderWriterLockSlim _flushLock;
private FenceHolder _flushFence;
private int _flushWaiting;
@@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Vulkan
_currentType = currentType;
DesiredType = currentType;
_flushLock = new ReaderWriterLock();
_flushLock = new ReaderWriterLockSlim();
_useMirrors = gd.IsTBDR;
}
@@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Vulkan
_currentType = currentType;
DesiredType = currentType;
_flushLock = new ReaderWriterLock();
_flushLock = new ReaderWriterLockSlim();
}
public bool TryBackingSwap(ref CommandBufferScoped? cbs)
@@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan
// Only swap if the buffer is not used in any queued command buffer.
bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool);
if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld && (_pendingData == null || cbs != null))
if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReadLockHeld && (_pendingData == null || cbs != null))
{
var currentAllocation = _allocationAuto;
var currentBuffer = _buffer;
@@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
ClearMirrors(cbs.Value, 0, Size);
}
_flushLock.AcquireWriterLock(Timeout.Infinite);
_flushLock.EnterWriteLock();
ClearFlushFence();
@@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Vulkan
_gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer);
_flushLock.ReleaseWriterLock();
_flushLock.ExitWriteLock();
}
_swapQueued = false;
@@ -548,42 +548,44 @@ namespace Ryujinx.Graphics.Vulkan
private void WaitForFlushFence()
{
// Assumes the _flushLock is held as reader, returns in same state.
if (_flushFence == null)
{
return;
}
// If storage has changed, make sure the fence has been reached so that the data is in place.
_flushLock.ExitReadLock();
_flushLock.EnterWriteLock();
if (_flushFence != null)
{
// If storage has changed, make sure the fence has been reached so that the data is in place.
var fence = _flushFence;
Interlocked.Increment(ref _flushWaiting);
var cookie = _flushLock.UpgradeToWriterLock(Timeout.Infinite);
// Don't wait in the lock.
if (_flushFence != null)
_flushLock.ExitWriteLock();
fence.Wait();
_flushLock.EnterWriteLock();
if (Interlocked.Decrement(ref _flushWaiting) == 0)
{
var fence = _flushFence;
Interlocked.Increment(ref _flushWaiting);
// Don't wait in the lock.
var restoreCookie = _flushLock.ReleaseLock();
fence.Wait();
_flushLock.RestoreLock(ref restoreCookie);
if (Interlocked.Decrement(ref _flushWaiting) == 0)
{
fence.Put();
}
_flushFence = null;
fence.Put();
}
_flushLock.DowngradeFromWriterLock(ref cookie);
_flushFence = null;
}
// Assumes the _flushLock is held as reader, returns in same state.
_flushLock.ExitWriteLock();
_flushLock.EnterReadLock();
}
public PinnedSpan<byte> GetData(int offset, int size)
{
_flushLock.AcquireReaderLock(Timeout.Infinite);
_flushLock.EnterReadLock();
WaitForFlushFence();
@@ -603,7 +605,7 @@ namespace Ryujinx.Graphics.Vulkan
// Need to be careful here, the buffer can't be unmapped while the data is being used.
_buffer.IncrementReferenceCount();
_flushLock.ReleaseReaderLock();
_flushLock.ExitReadLock();
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
}
@@ -621,7 +623,7 @@ namespace Ryujinx.Graphics.Vulkan
result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
}
_flushLock.ReleaseReaderLock();
_flushLock.ExitReadLock();
// Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses.
return PinnedSpan<byte>.UnsafeFromSpan(result);
@@ -1073,11 +1075,11 @@ namespace Ryujinx.Graphics.Vulkan
_allocationAuto.Dispose();
}
_flushLock.AcquireWriterLock(Timeout.Infinite);
_flushLock.EnterWriteLock();
ClearFlushFence();
_flushLock.ReleaseWriterLock();
_flushLock.ExitWriteLock();
}
}
}

View File

@@ -605,6 +605,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
supportsTextureShadowLod: false,
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex,

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Ins
{
[Service("ins:r")]
class IReceiverManager : IpcService
{
public IReceiverManager(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Ins
{
[Service("ins:s")]
class ISenderManager : IpcService
{
public ISenderManager(ServiceCtx context) { }
}
}

View File

@@ -29,6 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
private const bool IsDevelopment = false;
private readonly KEvent _stateChangeEvent;
private int _stateChangeEventHandle;
private NetworkState _state;
private DisconnectReason _disconnectReason;
@@ -277,12 +278,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
// AttachStateChangeEvent() -> handle<copy>
public ResultCode AttachStateChangeEvent(ServiceCtx context)
{
if (context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out int stateChangeEventHandle) != Result.Success)
if (_stateChangeEventHandle == 0 && context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(stateChangeEventHandle);
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangeEventHandle);
// Returns ResultCode.InvalidArgument if handle is null, doesn't occur in our case since we already throw an Exception.
@@ -964,6 +965,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
SetDisconnectReason(DisconnectReason.None);
}
if (_stateChangeEventHandle != 0)
{
context.Process.HandleTable.CloseHandle(_stateChangeEventHandle);
_stateChangeEventHandle = 0;
}
return resultCode;
}
@@ -1021,7 +1028,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
SetState(NetworkState.None);
NetworkClient?.DisconnectAndStop();
NetworkClient?.Dispose();
NetworkClient = null;
return ResultCode.Success;
@@ -1072,7 +1079,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
}
else
{
// NOTE: Service returns differents ResultCode here related to the nifm ResultCode.
// NOTE: Service returns different ResultCode here related to the nifm ResultCode.
resultCode = ResultCode.DeviceDisabled;
_nifmResultCode = resultCode;
}
@@ -1084,14 +1091,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
public void Dispose()
{
if (NetworkClient != null)
{
_station?.Dispose();
_accessPoint?.Dispose();
_station?.Dispose();
_station = null;
NetworkClient.DisconnectAndStop();
}
_accessPoint?.Dispose();
_accessPoint = null;
NetworkClient?.Dispose();
NetworkClient = null;
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Ovln
{
[Service("ovln:rcv")]
class IReceiverService : IpcService
{
public IReceiverService(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Ovln
{
[Service("ovln:snd")]
class ISenderService : IpcService
{
public ISenderService(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Psc
{
[Service("psc:c")]
class IPmControl : IpcService
{
public IPmControl(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Psc
{
[Service("psc:m")]
class IPmService : IpcService
{
public IPmService(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Psc
{
[Service("psc:l")] // 9.0.0+
class IPmUnknown : IpcService
{
public IPmUnknown(ServiceCtx context) { }
}
}

View File

@@ -1,9 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Srepo
{
[Service("srepo:a")] // 5.0.0+
[Service("srepo:u")] // 5.0.0+
class ISrepoService : IpcService
{
public ISrepoService(ServiceCtx context) { }
}
}

View File

@@ -1,9 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:hs")]
[Service("usb:hs:a")] // 7.0.0+
class IClientRootSession : IpcService
{
public IClientRootSession(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:ds")]
class IDsService : IpcService
{
public IDsService(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:pd:c")]
class IPdCradleManager : IpcService
{
public IPdCradleManager(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:pd")]
class IPdManager : IpcService
{
public IPdManager(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:pm")]
class IPmService : IpcService
{
public IPmService(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:qdb")] // 7.0.0+
class IUnknown1 : IpcService
{
public IUnknown1(ServiceCtx context) { }
}
}

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Usb
{
[Service("usb:obsv")] // 8.0.0+
class IUnknown2 : IpcService
{
public IUnknown2(ServiceCtx context) { }
}
}

View File

@@ -93,7 +93,7 @@ namespace Ryujinx.Horizon.Generators.Hipc
generator.LeaveScope();
generator.LeaveScope();
context.AddSource($"{className}.g.cs", generator.ToString());
context.AddSource($"{GetNamespaceName(commandInterface.ClassDeclarationSyntax)}.{className}.g.cs", generator.ToString());
}
}

View File

@@ -0,0 +1,47 @@
using Ryujinx.Horizon.Hshl.Ipc;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
namespace Ryujinx.Horizon.Hshl
{
class HshlIpcServer
{
private const int HshlMaxSessionsCount = 10;
private const int TotalMaxSessionsCount = HshlMaxSessionsCount * 2;
private const int PointerBufferSize = 0;
private const int MaxDomains = 0;
private const int MaxDomainObjects = 0;
private const int MaxPortsCount = 2;
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
private SmApi _sm;
private ServerManager _serverManager;
public void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, TotalMaxSessionsCount);
#pragma warning disable IDE0055 // Disable formatting
_serverManager.RegisterObjectForServer(new SetterManager(), ServiceName.Encode("hshl:set"), HshlMaxSessionsCount); // 11.0.0+
_serverManager.RegisterObjectForServer(new Manager(), ServiceName.Encode("hshl:sys"), HshlMaxSessionsCount); // 11.0.0+
#pragma warning restore IDE0055
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Ryujinx.Horizon.Hshl
{
class HshlMain : IService
{
public static void Main(ServiceTable serviceTable)
{
HshlIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Hshl;
namespace Ryujinx.Horizon.Hshl.Ipc
{
partial class Manager : IManager
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Hshl;
namespace Ryujinx.Horizon.Hshl.Ipc
{
partial class SetterManager : ISetterManager
{
}
}

View File

@@ -0,0 +1,47 @@
using Ryujinx.Horizon.Ins.Ipc;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
namespace Ryujinx.Horizon.Ins
{
class InsIpcServer
{
private const int InsMaxSessionsCount = 8;
private const int TotalMaxSessionsCount = InsMaxSessionsCount * 2;
private const int PointerBufferSize = 0x200;
private const int MaxDomains = 0;
private const int MaxDomainObjects = 0;
private const int MaxPortsCount = 2;
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
private SmApi _sm;
private ServerManager _serverManager;
public void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, TotalMaxSessionsCount);
#pragma warning disable IDE0055 // Disable formatting
_serverManager.RegisterObjectForServer(new ReceiverManager(), ServiceName.Encode("ins:r"), InsMaxSessionsCount); // 9.0.0+
_serverManager.RegisterObjectForServer(new SenderManager(), ServiceName.Encode("ins:s"), InsMaxSessionsCount); // 9.0.0+
#pragma warning restore IDE0055
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Ryujinx.Horizon.Ins
{
class InsMain : IService
{
public static void Main(ServiceTable serviceTable)
{
InsIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Ins;
namespace Ryujinx.Horizon.Ins.Ipc
{
partial class ReceiverManager : IReceiverManager
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Ins;
namespace Ryujinx.Horizon.Ins.Ipc
{
partial class SenderManager : ISenderManager
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Ovln;
namespace Ryujinx.Horizon.Ovln.Ipc
{
partial class ReceiverService : IReceiverService
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Ovln;
namespace Ryujinx.Horizon.Ovln.Ipc
{
partial class SenderService : ISenderService
{
}
}

View File

@@ -0,0 +1,48 @@
using Ryujinx.Horizon.Ovln.Ipc;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
namespace Ryujinx.Horizon.Ovln
{
class OvlnIpcServer
{
private const int OvlnRcvMaxSessionsCount = 2;
private const int OvlnSndMaxSessionsCount = 20;
private const int TotalMaxSessionsCount = OvlnRcvMaxSessionsCount + OvlnSndMaxSessionsCount;
private const int PointerBufferSize = 0;
private const int MaxDomains = 21;
private const int MaxDomainObjects = 60;
private const int MaxPortsCount = 2;
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
private SmApi _sm;
private ServerManager _serverManager;
public void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, TotalMaxSessionsCount);
#pragma warning disable IDE0055 // Disable formatting
_serverManager.RegisterObjectForServer(new ReceiverService(), ServiceName.Encode("ovln:rcv"), OvlnRcvMaxSessionsCount); // 8.0.0+
_serverManager.RegisterObjectForServer(new SenderService(), ServiceName.Encode("ovln:snd"), OvlnSndMaxSessionsCount); // 8.0.0+
#pragma warning restore IDE0055
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Ryujinx.Horizon.Ovln
{
class OvlnMain : IService
{
public static void Main(ServiceTable serviceTable)
{
OvlnIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Psc;
namespace Ryujinx.Horizon.Psc.Ipc
{
partial class PmControl : IPmControl
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Psc;
namespace Ryujinx.Horizon.Psc.Ipc
{
partial class PmService : IPmService
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Psc;
namespace Ryujinx.Horizon.Psc.Ipc
{
partial class PmStateLock : IPmStateLock
{
}
}

View File

@@ -0,0 +1,50 @@
using Ryujinx.Horizon.Psc.Ipc;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
namespace Ryujinx.Horizon.Psc
{
class PscIpcServer
{
private const int PscCMaxSessionsCount = 1;
private const int PscMMaxSessionsCount = 50;
private const int PscLMaxSessionsCount = 5;
private const int TotalMaxSessionsCount = PscCMaxSessionsCount + PscMMaxSessionsCount + PscLMaxSessionsCount;
private const int PointerBufferSize = 0;
private const int MaxDomains = 0;
private const int MaxDomainObjects = 0;
private const int MaxPortsCount = 3;
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
private SmApi _sm;
private ServerManager _serverManager;
public void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, TotalMaxSessionsCount);
#pragma warning disable IDE0055 // Disable formatting
_serverManager.RegisterObjectForServer(new PmControl(), ServiceName.Encode("psc:c"), PscCMaxSessionsCount);
_serverManager.RegisterObjectForServer(new PmService(), ServiceName.Encode("psc:m"), PscMMaxSessionsCount);
_serverManager.RegisterObjectForServer(new PmStateLock(), ServiceName.Encode("psc:l"), PscLMaxSessionsCount); // 9.0.0+
#pragma warning restore IDE0055
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Ryujinx.Horizon.Psc
{
class PscMain : IService
{
public static void Main(ServiceTable serviceTable)
{
PscIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Hshl
{
interface IManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Hshl
{
interface ISetterManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Ins
{
interface IReceiverManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Ins
{
interface ISenderManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Ovln
{
interface IReceiverService : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Ovln
{
interface ISenderService : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Psc
{
interface IPmControl : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Psc
{
interface IPmService : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Psc
{
interface IPmStateLock : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Srepo
{
interface ISrepoService : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IClientRootSession : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IDsRootSession : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IPdCradleManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IPdManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IPdManufactureManager : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IPmObserverService : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IPmService : IServiceObject
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Usb
{
interface IQdbManager : IServiceObject
{
}
}

View File

@@ -1,9 +1,15 @@
using Ryujinx.Horizon.Bcat;
using Ryujinx.Horizon.Hshl;
using Ryujinx.Horizon.Ins;
using Ryujinx.Horizon.Lbl;
using Ryujinx.Horizon.LogManager;
using Ryujinx.Horizon.MmNv;
using Ryujinx.Horizon.Ngc;
using Ryujinx.Horizon.Ovln;
using Ryujinx.Horizon.Prepo;
using Ryujinx.Horizon.Psc;
using Ryujinx.Horizon.Srepo;
using Ryujinx.Horizon.Usb;
using Ryujinx.Horizon.Wlan;
using System.Collections.Generic;
using System.Threading;
@@ -27,12 +33,18 @@ namespace Ryujinx.Horizon
}
RegisterService<BcatMain>();
RegisterService<HshlMain>();
RegisterService<InsMain>();
RegisterService<LblMain>();
RegisterService<LmMain>();
RegisterService<MmNvMain>();
RegisterService<PrepoMain>();
RegisterService<WlanMain>();
RegisterService<NgcMain>();
RegisterService<OvlnMain>();
RegisterService<PrepoMain>();
RegisterService<PscMain>();
RegisterService<SrepoMain>();
RegisterService<UsbMain>();
RegisterService<WlanMain>();
_totalServices = entries.Count;

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Srepo;
namespace Ryujinx.Horizon.Srepo.Ipc
{
partial class SrepoService : ISrepoService
{
}
}

View File

@@ -0,0 +1,46 @@
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
using Ryujinx.Horizon.Srepo.Ipc;
namespace Ryujinx.Horizon.Srepo
{
class SrepoIpcServer
{
private const int SrepoAMaxSessionsCount = 2;
private const int SrepoUMaxSessionsCount = 30;
private const int TotalMaxSessionsCount = SrepoAMaxSessionsCount + SrepoUMaxSessionsCount;
private const int PointerBufferSize = 0x80;
private const int MaxDomains = 32;
private const int MaxDomainObjects = 192;
private const int MaxPortsCount = 2;
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
private SmApi _sm;
private ServerManager _serverManager;
public void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, TotalMaxSessionsCount);
_serverManager.RegisterObjectForServer(new SrepoService(), ServiceName.Encode("srepo:a"), SrepoAMaxSessionsCount); // 5.0.0+
_serverManager.RegisterObjectForServer(new SrepoService(), ServiceName.Encode("srepo:u"), SrepoUMaxSessionsCount); // 5.0.0+
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Ryujinx.Horizon.Srepo
{
class SrepoMain : IService
{
public static void Main(ServiceTable serviceTable)
{
SrepoIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class ClientRootSession : IClientRootSession
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class DsRootSession : IDsRootSession
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class PdCradleManager : IPdCradleManager
{
}
}

View File

@@ -0,0 +1,9 @@
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class PdManager : IPdManager
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class PdManufactureManager : IPdManufactureManager
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class PmObserverService : IPmObserverService
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class PmService : IPmService
{
}
}

View File

@@ -0,0 +1,8 @@
using Ryujinx.Horizon.Sdk.Usb;
namespace Ryujinx.Horizon.Usb.Ipc
{
partial class QdbManager : IQdbManager
{
}
}

View File

@@ -0,0 +1,71 @@
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
using Ryujinx.Horizon.Usb.Ipc;
namespace Ryujinx.Horizon.Usb
{
class UsbIpcServer
{
private const int UsbDsMaxSessionsCount = 4;
private const int UsbHsMaxSessionsCount = 20;
private const int UsbHsAMaxSessionsCount = 3;
private const int UsbObsvMaxSessionsCount = 2;
private const int UsbPdMaxSessionsCount = 6;
private const int UsbPdCMaxSessionsCount = 4;
private const int UsbPdMMaxSessionsCount = 1;
private const int UsbPmMaxSessionsCount = 5;
private const int UsbQdbMaxSessionsCount = 4;
private const int TotalMaxSessionsCount =
UsbDsMaxSessionsCount +
UsbHsMaxSessionsCount +
UsbHsAMaxSessionsCount +
UsbObsvMaxSessionsCount +
UsbPdMaxSessionsCount +
UsbPdCMaxSessionsCount +
UsbPdMMaxSessionsCount +
UsbPmMaxSessionsCount +
UsbQdbMaxSessionsCount;
private const int PointerBufferSize = 0;
private const int MaxDomains = 0;
private const int MaxDomainObjects = 0;
private const int MaxPortsCount = 9;
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
private SmApi _sm;
private ServerManager _serverManager;
public void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, TotalMaxSessionsCount);
#pragma warning disable IDE0055 // Disable formatting
_serverManager.RegisterObjectForServer(new DsRootSession(), ServiceName.Encode("usb:ds"), UsbDsMaxSessionsCount);
_serverManager.RegisterObjectForServer(new ClientRootSession(), ServiceName.Encode("usb:hs"), UsbHsMaxSessionsCount);
_serverManager.RegisterObjectForServer(new ClientRootSession(), ServiceName.Encode("usb:hs:a"), UsbHsAMaxSessionsCount); // 7.0.0+
_serverManager.RegisterObjectForServer(new PmObserverService(), ServiceName.Encode("usb:obsv"), UsbObsvMaxSessionsCount); // 8.0.0+
_serverManager.RegisterObjectForServer(new PdManager(), ServiceName.Encode("usb:pd"), UsbPdMaxSessionsCount);
_serverManager.RegisterObjectForServer(new PdCradleManager(), ServiceName.Encode("usb:pd:c"), UsbPdCMaxSessionsCount);
_serverManager.RegisterObjectForServer(new PdManufactureManager(), ServiceName.Encode("usb:pd:m"), UsbPdMMaxSessionsCount); // 1.0.0
_serverManager.RegisterObjectForServer(new PmService(), ServiceName.Encode("usb:pm"), UsbPmMaxSessionsCount);
_serverManager.RegisterObjectForServer(new QdbManager(), ServiceName.Encode("usb:qdb"), UsbQdbMaxSessionsCount); // 7.0.0+
#pragma warning restore IDE0055
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Ryujinx.Horizon.Usb
{
class UsbMain : IService
{
public static void Main(ServiceTable serviceTable)
{
UsbIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}