Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b852c7481 | ||
|
c40c3905e2 | ||
|
a6cd044f0f | ||
|
f5a1de6ac5 | ||
|
2aeb5b00e3 | ||
|
60ba7b71f2 | ||
|
7c1d2bbb98 | ||
|
beacf8c1c8 | ||
|
0dbe45ae37 | ||
|
2b50e52e48 | ||
|
49eadbc209 | ||
|
2df16ded9b | ||
|
e43390c723 | ||
|
5af1327068 | ||
|
88a8d1e567 | ||
|
bf77d1cab9 |
@@ -36,7 +36,7 @@
|
||||
|
||||
## 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!
|
||||
|
||||
## Usage
|
||||
|
@@ -278,7 +278,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||
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())
|
||||
{
|
||||
|
@@ -295,8 +295,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
if (_validTzRegions.Contains(location))
|
||||
{
|
||||
TimeZone = location;
|
||||
|
||||
OnPropertyChanged(nameof(TimeZone));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
InitializeComponent();
|
||||
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 };
|
||||
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||
|
89
Ryujinx.Common/Memory/SpanOrArray.cs
Normal file
89
Ryujinx.Common/Memory/SpanOrArray.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// A struct that can represent both a Span and Array.
|
||||
/// This is useful to keep the Array representation when possible to avoid copies.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Element Type</typeparam>
|
||||
public ref struct SpanOrArray<T> where T : unmanaged
|
||||
{
|
||||
public readonly T[] Array;
|
||||
public readonly ReadOnlySpan<T> Span;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new SpanOrArray from an array.
|
||||
/// </summary>
|
||||
/// <param name="array">Array to store</param>
|
||||
public SpanOrArray(T[] array)
|
||||
{
|
||||
Array = array;
|
||||
Span = ReadOnlySpan<T>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new SpanOrArray from a readonly span.
|
||||
/// </summary>
|
||||
/// <param name="array">Span to store</param>
|
||||
public SpanOrArray(ReadOnlySpan<T> span)
|
||||
{
|
||||
Array = null;
|
||||
Span = span;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the contained array, or convert the span if necessary.
|
||||
/// </summary>
|
||||
/// <returns>An array containing the data</returns>
|
||||
public T[] ToArray()
|
||||
{
|
||||
return Array ?? Span.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a ReadOnlySpan from either the array or ReadOnlySpan.
|
||||
/// </summary>
|
||||
/// <returns>A ReadOnlySpan containing the data</returns>
|
||||
public ReadOnlySpan<T> AsSpan()
|
||||
{
|
||||
return Array ?? Span;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cast an array to a SpanOrArray.
|
||||
/// </summary>
|
||||
/// <param name="array">Source array</param>
|
||||
public static implicit operator SpanOrArray<T>(T[] array)
|
||||
{
|
||||
return new SpanOrArray<T>(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cast a ReadOnlySpan to a SpanOrArray.
|
||||
/// </summary>
|
||||
/// <param name="span">Source ReadOnlySpan</param>
|
||||
public static implicit operator SpanOrArray<T>(ReadOnlySpan<T> span)
|
||||
{
|
||||
return new SpanOrArray<T>(span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cast a Span to a SpanOrArray.
|
||||
/// </summary>
|
||||
/// <param name="span">Source Span</param>
|
||||
public static implicit operator SpanOrArray<T>(Span<T> span)
|
||||
{
|
||||
return new SpanOrArray<T>(span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cast a SpanOrArray to a ReadOnlySpan
|
||||
/// </summary>
|
||||
/// <param name="spanOrArray">Source SpanOrArray</param>
|
||||
public static implicit operator ReadOnlySpan<T>(SpanOrArray<T> spanOrArray)
|
||||
{
|
||||
return spanOrArray.AsSpan();
|
||||
}
|
||||
}
|
||||
}
|
@@ -129,7 +129,7 @@ namespace Ryujinx.Common
|
||||
|
||||
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)
|
||||
{
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
@@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.GAL
|
||||
ReadOnlySpan<byte> GetData();
|
||||
ReadOnlySpan<byte> GetData(int layer, int level);
|
||||
|
||||
void SetData(ReadOnlySpan<byte> data);
|
||||
void SetData(ReadOnlySpan<byte> data, int layer, int level);
|
||||
void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region);
|
||||
void SetData(SpanOrArray<byte> data);
|
||||
void SetData(SpanOrArray<byte> data, int layer, int level);
|
||||
void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region);
|
||||
void SetStorage(BufferRange buffer);
|
||||
void Release();
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using System;
|
||||
|
||||
@@ -107,19 +108,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data)
|
||||
public void SetData(SpanOrArray<byte> data)
|
||||
{
|
||||
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||
{
|
||||
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||
{
|
||||
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region);
|
||||
_renderer.QueueCommand();
|
||||
|
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
ReadOnlySpan<byte> data;
|
||||
byte[] data;
|
||||
if (srcLinear)
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
@@ -136,11 +137,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public LinkedListNode<Texture> CacheNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event to fire when texture data is disposed.
|
||||
/// </summary>
|
||||
public event Action<Texture> Disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Physical memory ranges where the texture data is located.
|
||||
/// </summary>
|
||||
@@ -720,9 +716,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
data = ConvertToHostCompatibleFormat(data);
|
||||
SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
|
||||
|
||||
HostTexture.SetData(data);
|
||||
HostTexture.SetData(result);
|
||||
|
||||
_hasData = true;
|
||||
}
|
||||
@@ -731,7 +727,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// Uploads new texture data to the host GPU.
|
||||
/// </summary>
|
||||
/// <param name="data">New data</param>
|
||||
public void SetData(ReadOnlySpan<byte> data)
|
||||
public void SetData(SpanOrArray<byte> data)
|
||||
{
|
||||
BlacklistScale();
|
||||
|
||||
@@ -750,7 +746,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="data">New data</param>
|
||||
/// <param name="layer">Target layer</param>
|
||||
/// <param name="level">Target level</param>
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||
{
|
||||
BlacklistScale();
|
||||
|
||||
@@ -786,7 +782,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="level">Mip level to convert</param>
|
||||
/// <param name="single">True to convert a single slice</param>
|
||||
/// <returns>Converted data</returns>
|
||||
public ReadOnlySpan<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||
public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||
{
|
||||
int width = Info.Width;
|
||||
int height = Info.Height;
|
||||
@@ -799,9 +795,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
height = Math.Max(height >> level, 1);
|
||||
depth = Math.Max(depth >> level, 1);
|
||||
|
||||
SpanOrArray<byte> result;
|
||||
|
||||
if (Info.IsLinear)
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||
result = LayoutConverter.ConvertLinearStridedToLinear(
|
||||
width,
|
||||
height,
|
||||
Info.FormatInfo.BlockWidth,
|
||||
@@ -813,7 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
else
|
||||
{
|
||||
data = LayoutConverter.ConvertBlockLinearToLinear(
|
||||
result = LayoutConverter.ConvertBlockLinearToLinear(
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
@@ -836,7 +834,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
|
||||
{
|
||||
if (!AstcDecoder.TryDecodeToRgba8P(
|
||||
data.ToArray(),
|
||||
result.ToArray(),
|
||||
Info.FormatInfo.BlockWidth,
|
||||
Info.FormatInfo.BlockHeight,
|
||||
width,
|
||||
@@ -856,11 +854,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers);
|
||||
}
|
||||
|
||||
data = decoded;
|
||||
result = decoded;
|
||||
}
|
||||
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
|
||||
{
|
||||
data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
|
||||
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result);
|
||||
}
|
||||
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
|
||||
{
|
||||
@@ -868,36 +866,36 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
case Format.Bc1RgbaSrgb:
|
||||
case Format.Bc1RgbaUnorm:
|
||||
data = BCnDecoder.DecodeBC1(data, width, height, depth, levels, layers);
|
||||
result = BCnDecoder.DecodeBC1(result, width, height, depth, levels, layers);
|
||||
break;
|
||||
case Format.Bc2Srgb:
|
||||
case Format.Bc2Unorm:
|
||||
data = BCnDecoder.DecodeBC2(data, width, height, depth, levels, layers);
|
||||
result = BCnDecoder.DecodeBC2(result, width, height, depth, levels, layers);
|
||||
break;
|
||||
case Format.Bc3Srgb:
|
||||
case Format.Bc3Unorm:
|
||||
data = BCnDecoder.DecodeBC3(data, width, height, depth, levels, layers);
|
||||
result = BCnDecoder.DecodeBC3(result, width, height, depth, levels, layers);
|
||||
break;
|
||||
case Format.Bc4Snorm:
|
||||
case Format.Bc4Unorm:
|
||||
data = BCnDecoder.DecodeBC4(data, width, height, depth, levels, layers, Format == Format.Bc4Snorm);
|
||||
result = BCnDecoder.DecodeBC4(result, width, height, depth, levels, layers, Format == Format.Bc4Snorm);
|
||||
break;
|
||||
case Format.Bc5Snorm:
|
||||
case Format.Bc5Unorm:
|
||||
data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
|
||||
result = BCnDecoder.DecodeBC5(result, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
|
||||
break;
|
||||
case Format.Bc6HSfloat:
|
||||
case Format.Bc6HUfloat:
|
||||
data = BCnDecoder.DecodeBC6(data, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
|
||||
result = BCnDecoder.DecodeBC6(result, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
|
||||
break;
|
||||
case Format.Bc7Srgb:
|
||||
case Format.Bc7Unorm:
|
||||
data = BCnDecoder.DecodeBC7(data, width, height, depth, levels, layers);
|
||||
result = BCnDecoder.DecodeBC7(result, width, height, depth, levels, layers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1445,7 +1443,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
DisposeTextures();
|
||||
|
||||
HostTexture = hostTexture;
|
||||
InvalidatedSequence++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1600,6 +1597,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
private void DisposeTextures()
|
||||
{
|
||||
InvalidatedSequence++;
|
||||
|
||||
_currentData = null;
|
||||
HostTexture.Release();
|
||||
|
||||
@@ -1634,8 +1633,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
DisposeTextures();
|
||||
|
||||
Disposed?.Invoke(this);
|
||||
|
||||
if (Group.Storage == this)
|
||||
{
|
||||
Group.Dispose();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.Cpu.Tracking;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Cpu.Tracking;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
@@ -348,9 +349,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
||||
|
||||
data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
||||
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
||||
|
||||
Storage.SetData(data, info.BaseLayer, info.BaseLevel);
|
||||
Storage.SetData(result, info.BaseLayer, info.BaseLevel);
|
||||
|
||||
offsetIndex++;
|
||||
}
|
||||
|
@@ -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 = 3732;
|
||||
private const uint CodeGenVersion = 3759;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
@@ -48,17 +49,19 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
return GetData();
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data)
|
||||
public void SetData(SpanOrArray<byte> data)
|
||||
{
|
||||
Buffer.SetData(_buffer, _bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize)));
|
||||
var dataSpan = data.AsSpan();
|
||||
|
||||
Buffer.SetData(_buffer, _bufferOffset, dataSpan.Slice(0, Math.Min(dataSpan.Length, _bufferSize)));
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
@@ -317,32 +318,36 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data)
|
||||
public void SetData(SpanOrArray<byte> data)
|
||||
{
|
||||
var dataSpan = data.AsSpan();
|
||||
|
||||
if (Format == Format.S8UintD24Unorm)
|
||||
{
|
||||
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
||||
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = data)
|
||||
fixed (byte* ptr = dataSpan)
|
||||
{
|
||||
ReadFrom((IntPtr)ptr, data.Length);
|
||||
ReadFrom((IntPtr)ptr, dataSpan.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||
{
|
||||
var dataSpan = data.AsSpan();
|
||||
|
||||
if (Format == Format.S8UintD24Unorm)
|
||||
{
|
||||
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
||||
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = data)
|
||||
fixed (byte* ptr = dataSpan)
|
||||
{
|
||||
int width = Math.Max(Info.Width >> level, 1);
|
||||
int height = Math.Max(Info.Height >> level, 1);
|
||||
@@ -352,11 +357,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||
{
|
||||
var dataSpan = data.AsSpan();
|
||||
|
||||
if (Format == Format.S8UintD24Unorm)
|
||||
{
|
||||
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
||||
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
|
||||
}
|
||||
|
||||
int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
|
||||
@@ -364,7 +371,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = data)
|
||||
fixed (byte* ptr = dataSpan)
|
||||
{
|
||||
ReadFrom2D(
|
||||
(IntPtr)ptr,
|
||||
|
@@ -377,6 +377,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
|
||||
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
|
||||
{
|
||||
HashSet<ulong> visited = new HashSet<ulong>();
|
||||
|
||||
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
|
||||
ulong baseOffset = lastOp.GetAbsoluteAddress();
|
||||
|
||||
@@ -392,9 +394,14 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
for (int i = 0; i < cbOffsetsCount; i++)
|
||||
{
|
||||
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
||||
Block target = getBlock(baseOffset + targetOffset);
|
||||
target.Predecessors.Add(block);
|
||||
block.Successors.Add(target);
|
||||
ulong targetAddress = baseOffset + targetOffset;
|
||||
|
||||
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));
|
||||
|
||||
// Sorting the target addresses in descending order improves the code,
|
||||
// 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);
|
||||
var targets = context.CurrBlock.Successors.Skip(startIndex);
|
||||
|
||||
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 (possibleTarget != lastTarget)
|
||||
if (++count < total && (target.Predecessors.Count > 1 || target.Address <= context.CurrBlock.Address))
|
||||
{
|
||||
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;
|
||||
|
||||
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 Dictionary<ulong, Operand> _labels;
|
||||
private readonly Dictionary<ulong, BlockLabel> _labels;
|
||||
|
||||
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
||||
{
|
||||
@@ -30,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
Config = config;
|
||||
IsNonMain = isNonMain;
|
||||
_operations = new List<Operation>();
|
||||
_labels = new Dictionary<ulong, Operand>();
|
||||
_labels = new Dictionary<ulong, BlockLabel>();
|
||||
|
||||
EmitStart();
|
||||
}
|
||||
@@ -158,14 +183,40 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public Operand GetLabel(ulong address)
|
||||
{
|
||||
if (!_labels.TryGetValue(address, out Operand label))
|
||||
{
|
||||
label = Label();
|
||||
return EnsureBlockLabel(address).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()
|
||||
|
@@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
context.CurrBlock = block;
|
||||
|
||||
context.MarkLabel(context.GetLabel(block.Address));
|
||||
context.EnterBlock(block.Address);
|
||||
|
||||
EmitOps(context, block);
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
};
|
||||
}
|
||||
|
||||
public static Span<byte> ConvertBlockLinearToLinear(
|
||||
public static byte[] ConvertBlockLinearToLinear(
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
@@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
blockHeight,
|
||||
bytesPerPixel);
|
||||
|
||||
Span<byte> output = new byte[outSize];
|
||||
byte[] output = new byte[outSize];
|
||||
|
||||
int outOffs = 0;
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
return output;
|
||||
}
|
||||
|
||||
public static Span<byte> ConvertLinearStridedToLinear(
|
||||
public static byte[] ConvertLinearStridedToLinear(
|
||||
int width,
|
||||
int height,
|
||||
int blockWidth,
|
||||
@@ -262,14 +262,15 @@ namespace Ryujinx.Graphics.Texture
|
||||
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
|
||||
lineSize = Math.Min(lineSize, outStride);
|
||||
|
||||
Span<byte> output = new byte[h * outStride];
|
||||
byte[] output = new byte[h * outStride];
|
||||
Span<byte> outSpan = output;
|
||||
|
||||
int outOffs = 0;
|
||||
int inOffs = 0;
|
||||
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
data.Slice(inOffs, lineSize).CopyTo(output.Slice(outOffs, lineSize));
|
||||
data.Slice(inOffs, lineSize).CopyTo(outSpan.Slice(outOffs, lineSize));
|
||||
|
||||
inOffs += stride;
|
||||
outOffs += outStride;
|
||||
|
@@ -386,8 +386,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_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)
|
||||
{
|
||||
if (!BoundToRange(offset, ref size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = new I8ToI16CacheKey(_gd);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!BoundToRange(offset, ref size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!BoundToRange(offset, ref size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
|
||||
|
||||
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
|
||||
|
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
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)
|
||||
|
@@ -49,7 +49,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
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;
|
||||
size = _size;
|
||||
|
@@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
using var emptyVb = gd.BufferManager.Create(gd, 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);
|
||||
|
||||
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);
|
||||
|
||||
_vertexBuffersDirty &= ~(1u << i);
|
||||
_vertexBuffersDirty &= ~(1UL << i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -64,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
};
|
||||
|
||||
samplerCreateInfo.PNext = &customBorderColor;
|
||||
samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
|
||||
}
|
||||
|
||||
gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -90,17 +91,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_bufferView = null;
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data)
|
||||
public void SetData(SpanOrArray<byte> data)
|
||||
{
|
||||
_gd.SetBufferData(_bufferHandle, _offset, data);
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -514,10 +515,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
dst.Info,
|
||||
srcRegion,
|
||||
dstRegion,
|
||||
src.FirstLevel,
|
||||
dst.FirstLevel,
|
||||
src.FirstLayer,
|
||||
dst.FirstLayer,
|
||||
src.FirstLevel,
|
||||
dst.FirstLevel,
|
||||
layers,
|
||||
levels,
|
||||
linearFilter,
|
||||
@@ -873,17 +874,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return GetDataFromBuffer(result, size, result);
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data)
|
||||
public void SetData(SpanOrArray<byte> data)
|
||||
{
|
||||
SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level)
|
||||
{
|
||||
SetData(data, layer, level, 1, 1, singleSlice: true);
|
||||
}
|
||||
|
||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
|
||||
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
|
||||
{
|
||||
SetData(data, layer, level, 1, 1, singleSlice: true, region);
|
||||
}
|
||||
|
@@ -57,37 +57,48 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
||||
cbs.CommandBuffer,
|
||||
binding,
|
||||
1,
|
||||
buffer,
|
||||
0,
|
||||
(ulong)(_size / _stride) * (ulong)stride,
|
||||
(ulong)stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
|
||||
var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
|
||||
|
||||
if (gd.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
||||
cbs.CommandBuffer,
|
||||
binding,
|
||||
1,
|
||||
buffer,
|
||||
0,
|
||||
(ulong)newSize,
|
||||
(ulong)stride);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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.
|
||||
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
|
||||
|
||||
if (_offset >= size)
|
||||
{
|
||||
autoBuffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
||||
return -1;
|
||||
}
|
||||
|
||||
return int.Parse(part.Substring(0, numberLength));
|
||||
return int.Parse(part.AsSpan(0, numberLength));
|
||||
}
|
||||
|
||||
private string ParseNumber(bool isSigned = false)
|
||||
|
@@ -2540,11 +2540,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
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(address).GetInfo();
|
||||
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
|
||||
|
||||
if (info.State != MemoryState.Unmapped)
|
||||
{
|
||||
@@ -2554,11 +2553,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
||||
ulong currEndAddr = info.Address + info.Size;
|
||||
|
||||
if (address >= regionStart &&
|
||||
address >= currBaseAddr &&
|
||||
endAddr - 1 <= regionEndAddr - 1 &&
|
||||
endAddr - 1 <= currEndAddr - 1)
|
||||
if (aslrAddress >= regionStart &&
|
||||
aslrAddress >= currBaseAddr &&
|
||||
aslrEndAddr - 1 <= regionEndAddr - 1 &&
|
||||
aslrEndAddr - 1 <= currEndAddr - 1)
|
||||
{
|
||||
address = aslrAddress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2603,7 +2603,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
|
||||
|
||||
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
|
||||
ulong regionEndAddr = (regionStart + regionPagesCount * PageSize) - 1;
|
||||
|
||||
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);
|
||||
|
||||
|
@@ -12,6 +12,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||
@@ -787,6 +788,26 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
||||
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)]
|
||||
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
||||
public ResultCode OpenDeviceOperator(ServiceCtx context)
|
||||
|
@@ -816,11 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
Reserved = new Array57<byte>()
|
||||
};
|
||||
|
||||
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
|
||||
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
|
||||
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
|
||||
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
|
||||
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), 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.AsSpan(4, 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.AsSpan(8, 4), NumberStyles.HexNumber);
|
||||
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber);
|
||||
|
||||
context.Memory.Write(outputPosition, modelInfo);
|
||||
|
||||
|
@@ -235,6 +235,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
if (!Socket.IsBound)
|
||||
{
|
||||
receiveSize = -1;
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
receiveSize = Socket.ReceiveFrom(buffer[..size], ConvertBsdSocketFlags(flags), ref temp);
|
||||
|
||||
remoteEndPoint = (IPEndPoint)temp;
|
||||
@@ -519,4 +526,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
|
||||
|
||||
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++)
|
||||
{
|
||||
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'))
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@@ -54,7 +54,7 @@ namespace Ryujinx.HLE.Utilities
|
||||
|
||||
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;
|
||||
|
@@ -51,7 +51,16 @@ namespace Ryujinx.Input.HLE
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
List<InputConfig> validInputs = new List<InputConfig>();
|
||||
|
||||
foreach (InputConfig inputConfigEntry in inputConfig)
|
||||
{
|
||||
NpadController controller = new NpadController(_cemuHookClient);
|
||||
@@ -116,6 +127,7 @@ namespace Ryujinx.Input.HLE
|
||||
else
|
||||
{
|
||||
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
|
||||
validInputs.Add(inputConfigEntry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +135,7 @@ namespace Ryujinx.Input.HLE
|
||||
_enableKeyboard = enableKeyboard;
|
||||
_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 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);
|
||||
|
||||
|
Reference in New Issue
Block a user