Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
beacf8c1c8 | ||
|
|
0dbe45ae37 | ||
|
|
2b50e52e48 | ||
|
|
49eadbc209 | ||
|
|
2df16ded9b |
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 3732;
|
private const uint CodeGenVersion = 3759;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|||||||
@@ -377,6 +377,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
|
|
||||||
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
|
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
|
||||||
{
|
{
|
||||||
|
HashSet<ulong> visited = new HashSet<ulong>();
|
||||||
|
|
||||||
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
|
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
|
||||||
ulong baseOffset = lastOp.GetAbsoluteAddress();
|
ulong baseOffset = lastOp.GetAbsoluteAddress();
|
||||||
|
|
||||||
@@ -392,9 +394,14 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
for (int i = 0; i < cbOffsetsCount; i++)
|
for (int i = 0; i < cbOffsetsCount; i++)
|
||||||
{
|
{
|
||||||
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
||||||
Block target = getBlock(baseOffset + targetOffset);
|
ulong targetAddress = baseOffset + targetOffset;
|
||||||
target.Predecessors.Add(block);
|
|
||||||
block.Successors.Add(target);
|
if (visited.Add(targetAddress))
|
||||||
|
{
|
||||||
|
Block target = getBlock(targetAddress);
|
||||||
|
target.Predecessors.Add(block);
|
||||||
|
block.Successors.Add(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,24 +41,82 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset));
|
Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset));
|
||||||
|
|
||||||
// Sorting the target addresses in descending order improves the code,
|
var targets = context.CurrBlock.Successors.Skip(startIndex);
|
||||||
// since it will always check the most distant targets first, then the
|
|
||||||
// near ones. This can be easily transformed into if/else statements.
|
|
||||||
var sortedTargets = context.CurrBlock.Successors.Skip(startIndex).OrderByDescending(x => x.Address);
|
|
||||||
|
|
||||||
Block lastTarget = sortedTargets.LastOrDefault();
|
bool allTargetsSinglePred = true;
|
||||||
|
int total = context.CurrBlock.Successors.Count - startIndex;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
foreach (Block possibleTarget in sortedTargets)
|
foreach (var target in targets.OrderBy(x => x.Address))
|
||||||
{
|
{
|
||||||
Operand label = context.GetLabel(possibleTarget.Address);
|
if (++count < total && (target.Predecessors.Count > 1 || target.Address <= context.CurrBlock.Address))
|
||||||
|
|
||||||
if (possibleTarget != lastTarget)
|
|
||||||
{
|
{
|
||||||
context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)possibleTarget.Address)));
|
allTargetsSinglePred = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
if (allTargetsSinglePred)
|
||||||
|
{
|
||||||
|
// Chain blocks, each target block will check if the BRX target address
|
||||||
|
// matches its own address, if not, it jumps to the next target which will do the same check,
|
||||||
|
// until it reaches the last possible target, which executed unconditionally.
|
||||||
|
// We can only do this if the BRX block is the only predecessor of all target blocks.
|
||||||
|
// Additionally, this is not supported for blocks located before the current block,
|
||||||
|
// since it will be too late to insert a label, but this is something that can be improved
|
||||||
|
// in the future if necessary.
|
||||||
|
|
||||||
|
var sortedTargets = targets.OrderBy(x => x.Address);
|
||||||
|
|
||||||
|
Block currentTarget = null;
|
||||||
|
ulong firstTargetAddress = 0;
|
||||||
|
|
||||||
|
foreach (Block nextTarget in sortedTargets)
|
||||||
{
|
{
|
||||||
context.Branch(label);
|
if (currentTarget != null)
|
||||||
|
{
|
||||||
|
if (currentTarget.Address != nextTarget.Address)
|
||||||
|
{
|
||||||
|
context.SetBrxTarget(currentTarget.Address, address, (int)currentTarget.Address, nextTarget.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firstTargetAddress = nextTarget.Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTarget = nextTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Branch(context.GetLabel(firstTargetAddress));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Emit the branches sequentially.
|
||||||
|
// This generates slightly worse code, but should work for all cases.
|
||||||
|
|
||||||
|
var sortedTargets = targets.OrderByDescending(x => x.Address);
|
||||||
|
ulong lastTargetAddress = ulong.MaxValue;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
foreach (Block target in sortedTargets)
|
||||||
|
{
|
||||||
|
Operand label = context.GetLabel(target.Address);
|
||||||
|
|
||||||
|
if (++count < total)
|
||||||
|
{
|
||||||
|
if (target.Address != lastTargetAddress)
|
||||||
|
{
|
||||||
|
context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)target.Address)));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTargetAddress = target.Address;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Branch(label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,33 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public int OperationsCount => _operations.Count;
|
public int OperationsCount => _operations.Count;
|
||||||
|
|
||||||
|
private struct BrxTarget
|
||||||
|
{
|
||||||
|
public readonly Operand Selector;
|
||||||
|
public readonly int ExpectedValue;
|
||||||
|
public readonly ulong NextTargetAddress;
|
||||||
|
|
||||||
|
public BrxTarget(Operand selector, int expectedValue, ulong nextTargetAddress)
|
||||||
|
{
|
||||||
|
Selector = selector;
|
||||||
|
ExpectedValue = expectedValue;
|
||||||
|
NextTargetAddress = nextTargetAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BlockLabel
|
||||||
|
{
|
||||||
|
public readonly Operand Label;
|
||||||
|
public BrxTarget BrxTarget;
|
||||||
|
|
||||||
|
public BlockLabel(Operand label)
|
||||||
|
{
|
||||||
|
Label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly List<Operation> _operations;
|
private readonly List<Operation> _operations;
|
||||||
private readonly Dictionary<ulong, Operand> _labels;
|
private readonly Dictionary<ulong, BlockLabel> _labels;
|
||||||
|
|
||||||
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
||||||
{
|
{
|
||||||
@@ -30,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
Config = config;
|
Config = config;
|
||||||
IsNonMain = isNonMain;
|
IsNonMain = isNonMain;
|
||||||
_operations = new List<Operation>();
|
_operations = new List<Operation>();
|
||||||
_labels = new Dictionary<ulong, Operand>();
|
_labels = new Dictionary<ulong, BlockLabel>();
|
||||||
|
|
||||||
EmitStart();
|
EmitStart();
|
||||||
}
|
}
|
||||||
@@ -158,14 +183,40 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public Operand GetLabel(ulong address)
|
public Operand GetLabel(ulong address)
|
||||||
{
|
{
|
||||||
if (!_labels.TryGetValue(address, out Operand label))
|
return EnsureBlockLabel(address).Label;
|
||||||
{
|
}
|
||||||
label = Label();
|
|
||||||
|
|
||||||
_labels.Add(address, label);
|
public void SetBrxTarget(ulong address, Operand selector, int targetValue, ulong nextTargetAddress)
|
||||||
|
{
|
||||||
|
BlockLabel blockLabel = EnsureBlockLabel(address);
|
||||||
|
Debug.Assert(blockLabel.BrxTarget.Selector == null);
|
||||||
|
blockLabel.BrxTarget = new BrxTarget(selector, targetValue, nextTargetAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnterBlock(ulong address)
|
||||||
|
{
|
||||||
|
BlockLabel blockLabel = EnsureBlockLabel(address);
|
||||||
|
|
||||||
|
MarkLabel(blockLabel.Label);
|
||||||
|
|
||||||
|
BrxTarget brxTarget = blockLabel.BrxTarget;
|
||||||
|
|
||||||
|
if (brxTarget.Selector != null)
|
||||||
|
{
|
||||||
|
this.BranchIfFalse(GetLabel(brxTarget.NextTargetAddress), this.ICompareEqual(brxTarget.Selector, Const(brxTarget.ExpectedValue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockLabel EnsureBlockLabel(ulong address)
|
||||||
|
{
|
||||||
|
if (!_labels.TryGetValue(address, out BlockLabel blockLabel))
|
||||||
|
{
|
||||||
|
blockLabel = new BlockLabel(Label());
|
||||||
|
|
||||||
|
_labels.Add(address, blockLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return label;
|
return blockLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareForVertexReturn()
|
public void PrepareForVertexReturn()
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
context.CurrBlock = block;
|
context.CurrBlock = block;
|
||||||
|
|
||||||
context.MarkLabel(context.GetLabel(block.Address));
|
context.EnterBlock(block.Address);
|
||||||
|
|
||||||
EmitOps(context, block);
|
EmitOps(context, block);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,8 +386,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_waitable.WaitForFences(_gd.Api, _device, offset, size);
|
_waitable.WaitForFences(_gd.Api, _device, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool BoundToRange(int offset, ref int size)
|
||||||
|
{
|
||||||
|
if (offset >= Size)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = Math.Min(Size - offset, size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
|
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
|
||||||
{
|
{
|
||||||
|
if (!BoundToRange(offset, ref size))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var key = new I8ToI16CacheKey(_gd);
|
var key = new I8ToI16CacheKey(_gd);
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||||
@@ -407,6 +424,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
|
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
|
||||||
{
|
{
|
||||||
|
if (!BoundToRange(offset, ref size))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
|
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||||
@@ -428,6 +450,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
|
||||||
{
|
{
|
||||||
|
if (!BoundToRange(offset, ref size))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
|
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
|
||||||
|
|
||||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public int GetPrimitiveCount(int vertexCount)
|
public int GetPrimitiveCount(int vertexCount)
|
||||||
{
|
{
|
||||||
return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
|
return Math.Max(0, (vertexCount - BaseIndex) / IndexStride);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetConvertedCount(int indexCount)
|
public int GetConvertedCount(int indexCount)
|
||||||
|
|||||||
@@ -49,7 +49,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
|
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int bufferSize);
|
||||||
|
|
||||||
|
if (_offset >= bufferSize)
|
||||||
|
{
|
||||||
|
autoBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
offset = _offset;
|
offset = _offset;
|
||||||
size = _size;
|
size = _size;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
|
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
|
||||||
emptyVb.SetData(0, new byte[EmptyVbSize]);
|
emptyVb.SetData(0, new byte[EmptyVbSize]);
|
||||||
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0);
|
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize, 0);
|
||||||
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
|
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
|
||||||
|
|
||||||
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
|
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
|
||||||
@@ -1243,7 +1243,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
|
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
|
||||||
|
|
||||||
_vertexBuffersDirty &= ~(1u << i);
|
_vertexBuffersDirty &= ~(1UL << i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,37 +57,48 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
|
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
|
||||||
{
|
{
|
||||||
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
|
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
|
||||||
int stride = (_stride + (alignment - 1)) & -alignment;
|
|
||||||
|
|
||||||
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
|
if (autoBuffer != null)
|
||||||
|
{
|
||||||
|
int stride = (_stride + (alignment - 1)) & -alignment;
|
||||||
|
int newSize = (_size / _stride) * stride;
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsExtendedDynamicState)
|
var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
|
||||||
{
|
|
||||||
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
if (gd.Capabilities.SupportsExtendedDynamicState)
|
||||||
cbs.CommandBuffer,
|
{
|
||||||
binding,
|
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
||||||
1,
|
cbs.CommandBuffer,
|
||||||
buffer,
|
binding,
|
||||||
0,
|
1,
|
||||||
(ulong)(_size / _stride) * (ulong)stride,
|
buffer,
|
||||||
(ulong)stride);
|
0,
|
||||||
}
|
(ulong)newSize,
|
||||||
else
|
(ulong)stride);
|
||||||
{
|
}
|
||||||
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
|
else
|
||||||
|
{
|
||||||
|
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer = autoBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer = autoBuffer;
|
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
||||||
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
|
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
|
||||||
|
|
||||||
// The original stride must be reapplied in case it was rewritten.
|
// The original stride must be reapplied in case it was rewritten.
|
||||||
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
||||||
|
|
||||||
|
if (_offset >= size)
|
||||||
|
{
|
||||||
|
autoBuffer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
|
|||||||
|
|
||||||
public bool Evaluate()
|
public bool Evaluate()
|
||||||
{
|
{
|
||||||
return (_input.Value & _mask) != 0;
|
return (_input.Value & _mask) == _mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,16 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_device.Hid.RefreshInputConfig(_inputConfig);
|
List<InputConfig> validInputs = new List<InputConfig>();
|
||||||
|
foreach (var inputConfigEntry in _inputConfig)
|
||||||
|
{
|
||||||
|
if (_controllers[(int)inputConfigEntry.PlayerIndex] != null)
|
||||||
|
{
|
||||||
|
validInputs.Add(inputConfigEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_device.Hid.RefreshInputConfig(validInputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +112,8 @@ namespace Ryujinx.Input.HLE
|
|||||||
_controllers[i] = null;
|
_controllers[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<InputConfig> validInputs = new List<InputConfig>();
|
||||||
|
|
||||||
foreach (InputConfig inputConfigEntry in inputConfig)
|
foreach (InputConfig inputConfigEntry in inputConfig)
|
||||||
{
|
{
|
||||||
NpadController controller = new NpadController(_cemuHookClient);
|
NpadController controller = new NpadController(_cemuHookClient);
|
||||||
@@ -116,6 +127,7 @@ namespace Ryujinx.Input.HLE
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
|
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
|
||||||
|
validInputs.Add(inputConfigEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +135,7 @@ namespace Ryujinx.Input.HLE
|
|||||||
_enableKeyboard = enableKeyboard;
|
_enableKeyboard = enableKeyboard;
|
||||||
_enableMouse = enableMouse;
|
_enableMouse = enableMouse;
|
||||||
|
|
||||||
_device.Hid.RefreshInputConfig(inputConfig);
|
_device.Hid.RefreshInputConfig(validInputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user