Compare commits

...

6 Commits

Author SHA1 Message Date
Mary
32450d45de vulkan: Clean up MemoryAllocator (#4418)
This started as an attempt to remove vkGetPhysicalDeviceMemoryProperties
in FindSuitableMemoryTypeIndex (As this could have some overhead and
shouldn't change at runtime) and turned in a little bigger cleanup.
2023-02-15 07:50:26 +01:00
Ac_K
ed7a0474c6 Infra: Issues template cleanup (#4421)
* Infra: Issues template cleanup

* applied
2023-02-14 15:58:57 +01:00
Mary
fe9c49949a vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level (#4408)
* vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level

This ensure we don't end up trying to initialize with anything currently incompatible.

* Address riperiperi's comment
2023-02-13 22:04:55 +00:00
Mary
052b23c83c vulkan: Do not call vkCmdSetViewport when viewportCount is 0 (#4406)
This fix validation error "VUID-vkCmdSetViewport-viewportCount-arraylength".
2023-02-13 20:32:20 +00:00
riperiperi
e4f68592c3 Fix partial updates for textures. (#4401)
I was forcing some types of texture to partially update when investigating performance with games that stream in data, and noticed that partially loading texture data was really broken on both backends.

Fixes Vulkan texture set by getting the correct expected size for the texture. Fixes partial upload on both backends for both Texture 2D Array and Cubemap using the wrong offset and uploading to the first layer/level for a handle. 3D might also be affected.

This might fix textures randomly having incorrect data in games that render to it - jumbled in the case of OpenGL, and outdated/black in the case of Vulkan. This case typically happens in UE4 games.
2023-02-12 10:30:26 +01:00
Logan Stromberg
1dcd44b94f Treat NpadIdType < 0 as invalid. Filter invalid SupportedPlayers inside IHidServer.SetSupportedNpadIdType(). (#4377)
Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2023-02-10 12:37:20 -03:00
15 changed files with 93 additions and 71 deletions

View File

@@ -1,26 +1,27 @@
name: Bug Report name: Bug Report
description: File a bug report description: File a bug report
title: "[Bug] <title>" title: "[Bug]"
labels: bug labels: bug
body: body:
- type: textarea - type: textarea
id: issue id: issue
attributes: attributes:
label: Description of Issue label: Description of the issue
description: What's the issue you encountered? description: What's the issue you encountered?
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: repro id: repro
attributes: attributes:
label: Reproduction Steps label: Reproduction steps
description: How can the issue be reproduced? description: How can the issue be reproduced?
placeholder: Describe each step as precisely as possible
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: log id: log
attributes: attributes:
label: Log File label: Log file
description: A log file will help our developers to better diagnose and fix the issue. description: A log file will help our developers to better diagnose and fix the issue.
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
validations: validations:
@@ -29,55 +30,44 @@ body:
id: os id: os
attributes: attributes:
label: OS label: OS
placeholder: "Example: Windows 10" placeholder: "e.g. Windows 10"
validations: validations:
required: true required: true
- type: input - type: input
id: ryujinx-version id: ryujinx-version
attributes: attributes:
label: Ryujinx version label: Ryujinx version
placeholder: | placeholder: "e.g. 1.0.470"
- *(e.g. 1.0.470)*
validations: validations:
required: true required: true
- type: input - type: input
id: game-version id: game-version
attributes: attributes:
label: Game version label: Game version
placeholder: | placeholder: "e.g. 1.1.1"
- *(e.g. 1.1.1)*
validations: validations:
required: false required: false
- type: input - type: input
id: cpu id: cpu
attributes: attributes:
label: CPU label: CPU
placeholder: | placeholder: "e.g. i7-6700"
- *(e.g. i7-6700)*
validations: validations:
required: false required: false
- type: input - type: input
id: gpu id: gpu
attributes: attributes:
label: GPU label: GPU
placeholder: | placeholder: "e.g. NVIDIA RTX 2070"
- *(e.g. NVIDIA RTX 2070)*
validations: validations:
required: false required: false
- type: input - type: input
id: ram id: ram
attributes: attributes:
label: RAM label: RAM
placeholder: | placeholder: "e.g. 16GB"
- *(e.g. 16GB)*
validations: validations:
required: false required: false
- type: checkboxes
attributes:
label: Applied Mods?
options:
- label: "Yes"
required: false
- type: textarea - type: textarea
id: mods id: mods
attributes: attributes:

View File

@@ -1,6 +1,6 @@
name: Feature Request name: Feature Request
description: Suggest a new feature for Ryujinx. description: Suggest a new feature for Ryujinx.
title: "[Feature Request] <title>" title: "[Feature Request]"
body: body:
- type: textarea - type: textarea
id: overview id: overview
@@ -12,14 +12,14 @@ body:
- type: textarea - type: textarea
id: details id: details
attributes: attributes:
label: Smaller Details label: Smaller details
description: These may include specific methods of implementation etc. description: These may include specific methods of implementation etc.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: request id: request
attributes: attributes:
label: Nature of Request label: Nature of request
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@@ -1,6 +1,6 @@
name: Missing CPU Instruction name: Missing CPU Instruction
description: CPU Instruction is missing in Ryujinx. description: CPU Instruction is missing in Ryujinx.
title: "[CPU] <title>" title: "[CPU]"
labels: [cpu, not-implemented] labels: [cpu, not-implemented]
body: body:
- type: textarea - type: textarea

View File

@@ -5,7 +5,7 @@ body:
- type: textarea - type: textarea
id: instruction id: instruction
attributes: attributes:
label: Service Call label: Service call
description: What service call is missing? description: What service call is missing?
validations: validations:
required: true required: true

View File

@@ -336,24 +336,23 @@ namespace Ryujinx.Graphics.Gpu.Image
if (_loadNeeded[baseHandle + i]) if (_loadNeeded[baseHandle + i])
{ {
var info = GetHandleInformation(baseHandle + i); var info = GetHandleInformation(baseHandle + i);
int offsetIndex = info.Index;
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views. // Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
for (int layer = 0; layer < info.Layers; layer++) for (int layer = 0; layer < info.Layers; layer++)
{ {
for (int level = 0; level < info.Levels; level++) for (int level = 0; level < info.Levels; level++)
{ {
int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
int offset = _allOffsets[offsetIndex]; int offset = _allOffsets[offsetIndex];
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size); int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
int size = endOffset - offset; int size = endOffset - offset;
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size)); ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true); SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
Storage.SetData(result, info.BaseLayer, info.BaseLevel); Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
offsetIndex++;
} }
} }
} }

View File

@@ -39,7 +39,6 @@ namespace Ryujinx.Graphics.Vulkan
BufferUsageFlags.VertexBufferBit | BufferUsageFlags.VertexBufferBit |
BufferUsageFlags.TransformFeedbackBufferBitExt; BufferUsageFlags.TransformFeedbackBufferBitExt;
private readonly PhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly IdList<BufferHolder> _buffers; private readonly IdList<BufferHolder> _buffers;
@@ -48,9 +47,8 @@ namespace Ryujinx.Graphics.Vulkan
public StagingBuffer StagingBuffer { get; } public StagingBuffer StagingBuffer { get; }
public BufferManager(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device) public BufferManager(VulkanRenderer gd, Device device)
{ {
_physicalDevice = physicalDevice;
_device = device; _device = device;
_buffers = new IdList<BufferHolder>(); _buffers = new IdList<BufferHolder>();
StagingBuffer = new StagingBuffer(gd, this); StagingBuffer = new StagingBuffer(gd, this);
@@ -114,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
allocateFlagsAlt = DefaultBufferMemoryAltFlags; allocateFlagsAlt = DefaultBufferMemoryAltFlags;
} }
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {

View File

@@ -9,34 +9,36 @@ namespace Ryujinx.Graphics.Vulkan
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
private readonly Vk _api; private readonly Vk _api;
private readonly PhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly List<MemoryAllocatorBlockList> _blockLists; private readonly List<MemoryAllocatorBlockList> _blockLists;
private readonly int _blockAlignment;
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
private int _blockAlignment; public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount)
public MemoryAllocator(Vk api, Device device, uint maxMemoryAllocationCount)
{ {
_api = api; _api = api;
_physicalDevice = physicalDevice;
_device = device; _device = device;
_blockLists = new List<MemoryAllocatorBlockList>(); _blockLists = new List<MemoryAllocatorBlockList>();
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags = 0) MemoryPropertyFlags flags = 0)
{ {
return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); return AllocateDeviceMemory(requirements, flags, flags);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags, MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags) MemoryPropertyFlags alternativeFlags)
{ {
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
if (memoryTypeIndex < 0) if (memoryTypeIndex < 0)
{ {
return default; return default;
@@ -65,20 +67,16 @@ namespace Ryujinx.Graphics.Vulkan
return newBl.Allocate(size, alignment, map); return newBl.Allocate(size, alignment, map);
} }
private static int FindSuitableMemoryTypeIndex( private int FindSuitableMemoryTypeIndex(
Vk api,
PhysicalDevice physicalDevice,
uint memoryTypeBits, uint memoryTypeBits,
MemoryPropertyFlags flags, MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags) MemoryPropertyFlags alternativeFlags)
{ {
int bestCandidateIndex = -1; int bestCandidateIndex = -1;
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
for (int i = 0; i < properties.MemoryTypeCount; i++)
{ {
var type = properties.MemoryTypes[i]; var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0) if ((memoryTypeBits & (1 << i)) != 0)
{ {

View File

@@ -650,9 +650,7 @@ namespace Ryujinx.Graphics.Vulkan
_newState.DepthWriteEnable = oldDepthWriteEnable; _newState.DepthWriteEnable = oldDepthWriteEnable;
_newState.Topology = oldTopology; _newState.Topology = oldTopology;
DynamicState.Viewports = oldViewports; DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
DynamicState.ViewportsCount = (int)oldViewportsCount;
DynamicState.SetViewportsDirty();
_newState.ViewportsCount = oldViewportsCount; _newState.ViewportsCount = oldViewportsCount;
SignalStateChange(); SignalStateChange();
@@ -1183,6 +1181,8 @@ namespace Ryujinx.Graphics.Vulkan
return Math.Clamp(value, 0f, 1f); return Math.Clamp(value, 0f, 1f);
} }
DynamicState.ViewportsCount = (uint)count;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
var viewport = viewports[i]; var viewport = viewports[i];
@@ -1196,8 +1196,6 @@ namespace Ryujinx.Graphics.Vulkan
Clamp(viewport.DepthFar))); Clamp(viewport.DepthFar)));
} }
DynamicState.ViewportsCount = count;
float disableTransformF = disableTransform ? 1.0f : 0.0f; float disableTransformF = disableTransform ? 1.0f : 0.0f;
if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform) if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform)
{ {

View File

@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
private Array4<float> _blendConstants; private Array4<float> _blendConstants;
public int ViewportsCount; public uint ViewportsCount;
public Array16<Viewport> Viewports; public Array16<Viewport> Viewports;
private enum DirtyFlags private enum DirtyFlags
@@ -88,10 +88,16 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.Viewport; _dirty |= DirtyFlags.Viewport;
} }
public void SetViewportsDirty() public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount)
{
Viewports = viewports;
ViewportsCount = viewportsCount;
if (ViewportsCount != 0)
{ {
_dirty |= DirtyFlags.Viewport; _dirty |= DirtyFlags.Viewport;
} }
}
public void ForceAllDirty() public void ForceAllDirty()
{ {
@@ -155,7 +161,10 @@ namespace Ryujinx.Graphics.Vulkan
private void RecordViewport(Vk api, CommandBuffer commandBuffer) private void RecordViewport(Vk api, CommandBuffer commandBuffer)
{ {
api.CmdSetViewport(commandBuffer, 0, (uint)ViewportsCount, Viewports.AsSpan()); if (ViewportsCount != 0)
{
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
} }
} }
} }

View File

@@ -55,7 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe TextureStorage( public unsafe TextureStorage(
VulkanRenderer gd, VulkanRenderer gd,
PhysicalDevice physicalDevice,
Device device, Device device,
TextureCreateInfo info, TextureCreateInfo info,
float scaleFactor, float scaleFactor,
@@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan
if (foreignAllocation == null) if (foreignAllocation == null)
{ {
gd.Api.GetImageMemoryRequirements(device, _image, out var requirements); gd.Api.GetImageMemoryRequirements(device, _image, out var requirements);
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(physicalDevice, requirements, DefaultImageMemoryFlags); var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, DefaultImageMemoryFlags);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {
@@ -173,7 +172,7 @@ namespace Ryujinx.Graphics.Vulkan
var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel);
storage = new TextureStorage(_gd, default, _device, info, ScaleFactor, _allocationAuto); storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto);
_aliasedStorages.Add(format, storage); _aliasedStorages.Add(format, storage);
} }

View File

@@ -712,7 +712,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int level = 0; level < levels; level++) for (int level = 0; level < levels; level++)
{ {
int mipSize = GetBufferDataLength(Info.GetMipSize(dstLevel + level)); int mipSize = GetBufferDataLength(Info.GetMipSize2D(dstLevel + level) * dstLayers);
int endOffset = offset + mipSize; int endOffset = offset + mipSize;

View File

@@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe static class VulkanInitialization public unsafe static class VulkanInitialization
{ {
private const uint InvalidIndex = uint.MaxValue; private const uint InvalidIndex = uint.MaxValue;
private static uint MinimalVulkanVersion = Vk.Version11.Value;
private static uint MinimalInstanceVulkanVersion = Vk.Version12.Value;
private static uint MaximumVulkanVersion = Vk.Version12.Value;
private const string AppName = "Ryujinx.Graphics.Vulkan"; private const string AppName = "Ryujinx.Graphics.Vulkan";
private const int QueuesCount = 2; private const int QueuesCount = 2;
@@ -99,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
ApplicationVersion = 1, ApplicationVersion = 1,
PEngineName = (byte*)appName, PEngineName = (byte*)appName,
EngineVersion = 1, EngineVersion = 1,
ApiVersion = Vk.Version12.Value ApiVersion = MaximumVulkanVersion
}; };
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
@@ -224,7 +227,7 @@ namespace Ryujinx.Graphics.Vulkan
ApplicationVersion = 1, ApplicationVersion = 1,
PEngineName = (byte*)appName, PEngineName = (byte*)appName,
EngineVersion = 1, EngineVersion = 1,
ApiVersion = Vk.Version12.Value ApiVersion = MaximumVulkanVersion
}; };
var instanceCreateInfo = new InstanceCreateInfo var instanceCreateInfo = new InstanceCreateInfo
@@ -239,6 +242,27 @@ namespace Ryujinx.Graphics.Vulkan
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
// We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
// If the instance doesn't support it, no device is going to be 1.1 compatible.
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
// We currently assume that the instance is compatible with Vulkan 1.2
// TODO: Remove this once we relax our initialization codepaths.
uint instanceApiVerison = 0;
api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
if (instanceApiVerison < MinimalInstanceVulkanVersion)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
Marshal.FreeHGlobal(appName); Marshal.FreeHGlobal(appName);
uint physicalDeviceCount; uint physicalDeviceCount;
@@ -259,6 +283,11 @@ namespace Ryujinx.Graphics.Vulkan
var physicalDevice = physicalDevices[i]; var physicalDevice = physicalDevices[i];
api.GetPhysicalDeviceProperties(physicalDevice, out var properties); api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (properties.ApiVersion < MinimalVulkanVersion)
{
continue;
}
devices[i] = new DeviceInfo( devices[i] = new DeviceInfo(
StringFromIdPair(properties.VendorID, properties.DeviceID), StringFromIdPair(properties.VendorID, properties.DeviceID),
VendorUtils.GetNameFromId(properties.VendorID), VendorUtils.GetNameFromId(properties.VendorID),

View File

@@ -280,7 +280,7 @@ namespace Ryujinx.Graphics.Vulkan
supportedSampleCounts, supportedSampleCounts,
portabilityFlags); portabilityFlags);
MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
@@ -290,7 +290,7 @@ namespace Ryujinx.Graphics.Vulkan
BackgroundResources = new BackgroundResources(this, _device); BackgroundResources = new BackgroundResources(this, _device);
BufferManager = new BufferManager(this, _physicalDevice, _device); BufferManager = new BufferManager(this, _device);
_syncManager = new SyncManager(this, _device); _syncManager = new SyncManager(this, _device);
_pipeline = new PipelineFull(this, _device); _pipeline = new PipelineFull(this, _device);
@@ -388,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan
internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale) internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale)
{ {
return new TextureStorage(this, _physicalDevice, _device, info, scale); return new TextureStorage(this, _device, info, scale);
} }
public void DeleteBuffer(BufferHandle buffer) public void DeleteBuffer(BufferHandle buffer)

View File

@@ -38,7 +38,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
public static bool IsValidNpadIdType(NpadIdType npadIdType) public static bool IsValidNpadIdType(NpadIdType npadIdType)
{ {
return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown; return (npadIdType >= NpadIdType.Player1 && npadIdType <= NpadIdType.Player8) ||
npadIdType == NpadIdType.Handheld ||
npadIdType == NpadIdType.Unknown;
} }
} }
} }

View File

@@ -722,7 +722,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
for (int i = 0; i < supportedPlayerIds.Length; ++i) for (int i = 0; i < supportedPlayerIds.Length; ++i)
{ {
if (supportedPlayerIds[i] >= 0) if (HidUtils.IsValidNpadIdType(supportedPlayerIds[i]))
{ {
context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i])); context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i]));
} }
@@ -1101,7 +1101,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey) if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
{ {
if (npadIdType >= (NpadIdType.Player8 + 1) && npadIdType != NpadIdType.Handheld && npadIdType != NpadIdType.Unknown) if (!HidUtils.IsValidNpadIdType(npadIdType))
{ {
return ResultCode.InvalidNpadIdType; return ResultCode.InvalidNpadIdType;
} }