Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
77c4291c34 | |||
6e92b7a378 | |||
9b852c7481 | |||
c40c3905e2 | |||
a6cd044f0f | |||
f5a1de6ac5 | |||
2aeb5b00e3 | |||
60ba7b71f2 | |||
7c1d2bbb98 | |||
beacf8c1c8 | |||
0dbe45ae37 | |||
2b50e52e48 | |||
49eadbc209 | |||
2df16ded9b |
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||||
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -588,5 +588,9 @@
|
|||||||
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
||||||
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
|
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
|
||||||
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
|
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
|
||||||
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?"
|
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?",
|
||||||
|
"RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?",
|
||||||
|
"SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:",
|
||||||
|
"SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:",
|
||||||
|
"VolumeShort": "Głoś"
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||||
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray());
|
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows())
|
if (!OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
|
@ -295,8 +295,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
if (_validTzRegions.Contains(location))
|
if (_validTzRegions.Contains(location))
|
||||||
{
|
{
|
||||||
TimeZone = location;
|
TimeZone = location;
|
||||||
|
|
||||||
OnPropertyChanged(nameof(TimeZone));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{ReflectionBinding ChangeLanguage}"
|
Command="{ReflectionBinding ChangeLanguage}"
|
||||||
CommandParameter="pl_PL"
|
CommandParameter="pl_PL"
|
||||||
Header="Polish" />
|
Header="Polski" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{ReflectionBinding ChangeLanguage}"
|
Command="{ReflectionBinding ChangeLanguage}"
|
||||||
CommandParameter="ru_RU"
|
CommandParameter="ru_RU"
|
||||||
|
@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Load();
|
Load();
|
||||||
|
|
||||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
|
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
|
||||||
MultiBinding tzMultiBinding = new() { Converter = converter };
|
MultiBinding tzMultiBinding = new() { Converter = converter };
|
||||||
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
||||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||||
|
@ -129,7 +129,7 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
private static (Assembly, string) ResolveManifestPath(string filename)
|
private static (Assembly, string) ResolveManifestPath(string filename)
|
||||||
{
|
{
|
||||||
var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
if (segments.Length >= 2)
|
if (segments.Length >= 2)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,6 +480,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (--_viewsCount == 0)
|
if (--_viewsCount == 0)
|
||||||
{
|
{
|
||||||
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
|
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
|
||||||
|
|
||||||
|
Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,10 +515,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dst.Info,
|
dst.Info,
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
src.FirstLevel,
|
|
||||||
dst.FirstLevel,
|
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
|
src.FirstLevel,
|
||||||
|
dst.FirstLevel,
|
||||||
layers,
|
layers,
|
||||||
levels,
|
levels,
|
||||||
linearFilter,
|
linearFilter,
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal TextureView CreateTextureView(TextureCreateInfo info, float scale)
|
internal TextureView CreateTextureView(TextureCreateInfo info, float scale)
|
||||||
{
|
{
|
||||||
// This should be disposed when all views are destroyed.
|
// This should be disposed when all views are destroyed.
|
||||||
using var storage = CreateTextureStorage(info, scale);
|
var storage = CreateTextureStorage(info, scale);
|
||||||
return storage.CreateView(info, 0, 0);
|
return storage.CreateView(info, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return int.Parse(part.Substring(0, numberLength));
|
return int.Parse(part.AsSpan(0, numberLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseNumber(bool isSigned = false)
|
private string ParseNumber(bool isSigned = false)
|
||||||
|
@ -2540,11 +2540,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
for (int attempt = 0; attempt < 8; attempt++)
|
for (int attempt = 0; attempt < 8; attempt++)
|
||||||
{
|
{
|
||||||
address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
|
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
|
||||||
|
ulong aslrEndAddr = aslrAddress + totalNeededSize;
|
||||||
|
|
||||||
ulong endAddr = address + totalNeededSize;
|
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
|
||||||
|
|
||||||
KMemoryInfo info = _blockManager.FindBlock(address).GetInfo();
|
|
||||||
|
|
||||||
if (info.State != MemoryState.Unmapped)
|
if (info.State != MemoryState.Unmapped)
|
||||||
{
|
{
|
||||||
@ -2554,11 +2553,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
||||||
ulong currEndAddr = info.Address + info.Size;
|
ulong currEndAddr = info.Address + info.Size;
|
||||||
|
|
||||||
if (address >= regionStart &&
|
if (aslrAddress >= regionStart &&
|
||||||
address >= currBaseAddr &&
|
aslrAddress >= currBaseAddr &&
|
||||||
endAddr - 1 <= regionEndAddr - 1 &&
|
aslrEndAddr - 1 <= regionEndAddr - 1 &&
|
||||||
endAddr - 1 <= currEndAddr - 1)
|
aslrEndAddr - 1 <= currEndAddr - 1)
|
||||||
{
|
{
|
||||||
|
address = aslrAddress;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2603,7 +2603,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
|
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
|
||||||
|
|
||||||
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
|
ulong regionEndAddr = (regionStart + regionPagesCount * PageSize) - 1;
|
||||||
|
|
||||||
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);
|
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||||
@ -787,6 +788,26 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(205)]
|
||||||
|
// OpenDataStorageWithProgramIndex(u8 program_index) -> object<nn::fssrv::sf::IStorage>
|
||||||
|
public ResultCode OpenDataStorageWithProgramIndex(ServiceCtx context)
|
||||||
|
{
|
||||||
|
byte programIndex = context.RequestData.ReadByte();
|
||||||
|
|
||||||
|
if ((context.Device.Application.TitleId & 0xf) != programIndex)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
var storage = context.Device.FileSystem.RomFs.AsStorage(true);
|
||||||
|
using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage);
|
||||||
|
using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref()));
|
||||||
|
|
||||||
|
MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref()));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(400)]
|
[CommandHipc(400)]
|
||||||
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
||||||
public ResultCode OpenDeviceOperator(ServiceCtx context)
|
public ResultCode OpenDeviceOperator(ServiceCtx context)
|
||||||
|
@ -816,11 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||||||
Reserved = new Array57<byte>()
|
Reserved = new Array57<byte>()
|
||||||
};
|
};
|
||||||
|
|
||||||
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
|
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber));
|
||||||
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
|
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber);
|
||||||
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
|
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber);
|
||||||
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
|
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber);
|
||||||
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
|
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber);
|
||||||
|
|
||||||
context.Memory.Write(outputPosition, modelInfo);
|
context.Memory.Write(outputPosition, modelInfo);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,9 +122,8 @@ namespace Ryujinx.HLE.HOS.Tamper
|
|||||||
for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++)
|
for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++)
|
||||||
{
|
{
|
||||||
int index = wordIndex * wordSize + nybbleIndex;
|
int index = wordIndex * wordSize + nybbleIndex;
|
||||||
string byteData = word.Substring(nybbleIndex, 1);
|
|
||||||
|
|
||||||
instruction[index] = byte.Parse(byteData, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
instruction[index] = byte.Parse(word.AsSpan(nybbleIndex, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ namespace Ryujinx.HLE.Loaders.Mods
|
|||||||
{
|
{
|
||||||
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|
||||||
{
|
{
|
||||||
return int.TryParse(str.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out value);
|
return int.TryParse(str.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
|
|
||||||
for (int index = 0; index < bytesInHex; index++)
|
for (int index = 0; index < bytesInHex; index++)
|
||||||
{
|
{
|
||||||
output[index] = byte.Parse(hexString.Substring(index * 2, 2), NumberStyles.HexNumber);
|
output[index] = byte.Parse(hexString.AsSpan(index * 2, 2), NumberStyles.HexNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
||||||
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||||
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
|
var ryuArg = Environment.GetCommandLineArgs().AsEnumerable().Skip(1);
|
||||||
|
|
||||||
Process.Start(ryuExe, ryuArg);
|
Process.Start(ryuExe, ryuArg);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user