Compare commits

...

6 Commits

Author SHA1 Message Date
TSRBerry
3d1a0bf374 [Headless] Fix for not receiving any SDL events on Linux (#4182)
* headless: Fix SDL events not working

* Add nuget.config
2022-12-29 15:09:18 +01:00
Mary-nyan
c20f3fbebd haydn: Add support for PCMFloat, PCM32 and PCM8 conversions (#4100)
* haydn: Add support for PCMFloat, PCM32 and PCM8 conversions

This adds support in the compatibility layer for other sample format
than PCM16.

This should help extends compatibility with soundio on devices that
doesn't expose PCM16.

I ommited PCM24 conversion for now as it's not simplest of all.

* Address TSRBerry's comment

* Address comments

* Fix conversion issue and clean up saturation usage

* Revert saturation changes

* Address gdkchan's comment
2022-12-28 19:34:30 -03:00
Berkan Diler
0d3b82477e Use new ArgumentNullException and ObjectDisposedException throw-helper API (#4163) 2022-12-27 20:27:11 +01:00
riperiperi
470be03c2f GPU: Add fallback when 16-bit formats are not supported (#4108)
* Add conversion for 16 bit RGBA formats (not supported in Rosetta)

* Rebase fix

Rebase fix

* Forgot to remove this

* Fix RGBA16 format conversion

* Add RGBA4 -> RGBA8 conversion

* Handle host stride alignment

* Address Feedback Part 1

* Can't count

* Don't zero out rgb when alpha is 0

* Separate RGBA4 and 5-bit component formats

Not sure of a better way to name them...

* Add A1B5G5R5 conversion

* Put this in the right place.

* Make format naming consistent for capabilities

* Change method names
2022-12-26 15:50:27 -03:00
Hunter
c963b3c804 Added Generic Math to BitUtils (#3929)
* Generic Math Update

Updated Several functions in Ryujinx.Common/Utilities/BitUtils to use generic math

* Updated BitUtil calls

* Removed Whitespace

* Switched decrement

* Fixed changed method calls.

The method calls were originally changed on accident due to me relying too much on intellisense doing stuff for me

* Update Ryujinx.Common/Utilities/BitUtils.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-26 14:11:05 +00:00
gnisman
a4fdfb5f94 bsd::RecvFrom: verify output buffer size before writing socket address (#4135)
* bsd::RecvFrom: Ryujinx does not verify output buffer size before writing socket address

* Calculate the size of BsdSockAddr

* use bsdSockAddr variable

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* set errno to ENOMEM in case we can't write the address to memory

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-26 13:49:26 +00:00
48 changed files with 587 additions and 373 deletions

3
.gitignore vendored
View File

@@ -125,6 +125,9 @@ ClientBin/
packages/*
*.config
# Include nuget.config
!nuget.config
# RIA/Silverlight projects
Generated_Code/

View File

@@ -80,10 +80,7 @@ namespace ARMeilleure.Common
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_pages)
{
@@ -100,10 +97,7 @@ namespace ARMeilleure.Common
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public AddressTable(Level[] levels)
{
if (levels == null)
{
throw new ArgumentNullException(nameof(levels));
}
ArgumentNullException.ThrowIfNull(levels);
if (levels.Length < 2)
{
@@ -141,10 +135,7 @@ namespace ARMeilleure.Common
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
public ref TEntry GetValue(ulong address)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
if (!IsValid(address))
{

View File

@@ -49,10 +49,7 @@ namespace ARMeilleure.Common
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return ref _countTable.GetValue(_index);
}

View File

@@ -53,10 +53,7 @@ namespace ARMeilleure.Common
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public int Allocate()
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_allocated)
{
@@ -83,10 +80,7 @@ namespace ARMeilleure.Common
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public void Free(int index)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_allocated)
{
@@ -108,10 +102,7 @@ namespace ARMeilleure.Common
/// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
public ref TEntry GetValue(int index)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_allocated)
{

View File

@@ -48,10 +48,7 @@ namespace ARMeilleure.IntermediateRepresentation
public void AddSuccessor(BasicBlock block)
{
if (block == null)
{
ThrowNull(nameof(block));
}
ArgumentNullException.ThrowIfNull(block);
if ((uint)_succCount + 1 > MaxSuccessors)
{
@@ -100,10 +97,7 @@ namespace ARMeilleure.IntermediateRepresentation
public void SetSuccessor(int index, BasicBlock block)
{
if (block == null)
{
ThrowNull(nameof(block));
}
ArgumentNullException.ThrowIfNull(block);
if ((uint)index >= (uint)_succCount)
{
@@ -144,7 +138,6 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
private static void ThrowNull(string name) => throw new ArgumentNullException(name);
private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");

View File

@@ -25,10 +25,7 @@ namespace ARMeilleure.Translation
public static Delegate GetDelegate(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
Type returnType = info.ReturnType;

View File

@@ -35,10 +35,7 @@ namespace ARMeilleure.Translation
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
string key = GetKey(info);
@@ -52,10 +49,7 @@ namespace ARMeilleure.Translation
public static int GetDelegateIndex(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
string key = GetKey(info);

View File

@@ -67,10 +67,7 @@ namespace ARMeilleure.Translation
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(value);
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
}
@@ -85,10 +82,7 @@ namespace ARMeilleure.Translation
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
public V GetOrAdd(K start, K end, V value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(value);
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
return node.Value;
@@ -152,10 +146,7 @@ namespace ARMeilleure.Translation
/// <returns>Node reference in the tree</returns>
private IntervalTreeNode<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
IntervalTreeNode<K, V> node = _root;
while (node != null)

View File

@@ -30,10 +30,7 @@ namespace ARMeilleure.Translation
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return _dispatchStub.Value;
}
@@ -47,10 +44,7 @@ namespace ARMeilleure.Translation
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return _slowDispatchStub.Value;
}
@@ -64,10 +58,7 @@ namespace ARMeilleure.Translation
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return _dispatchLoop.Value;
}
@@ -81,7 +72,9 @@ namespace ARMeilleure.Translation
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
public TranslatorStubs(Translator translator)
{
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
ArgumentNullException.ThrowIfNull(translator);
_translator = translator;
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
}

View File

@@ -51,6 +51,40 @@ namespace Ryujinx.Audio.Backends.CompatLayer
};
}
private SampleFormat SelectHardwareSampleFormat(SampleFormat targetSampleFormat)
{
if (_realDriver.SupportsSampleFormat(targetSampleFormat))
{
return targetSampleFormat;
}
// Attempt conversion from PCM16.
if (targetSampleFormat == SampleFormat.PcmInt16)
{
// Prefer PCM32 if we need to convert.
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt32))
{
return SampleFormat.PcmInt32;
}
// If not supported, PCM float provides the best quality with a cost lower than PCM24.
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmFloat))
{
return SampleFormat.PcmFloat;
}
// TODO: Implement PCM24 conversion.
// If nothing is truly supported, attempt PCM8 at the cost of loosing quality.
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt8))
{
return SampleFormat.PcmInt8;
}
}
throw new ArgumentException("No valid sample format configuration found!");
}
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
{
if (channelCount == 0)
@@ -77,15 +111,26 @@ namespace Ryujinx.Audio.Backends.CompatLayer
throw new NotImplementedException();
}
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, sampleFormat, sampleRate, hardwareChannelCount, volume);
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount, volume);
if (hardwareChannelCount == channelCount)
if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat)
{
return realSession;
}
if (hardwareSampleFormat != sampleFormat)
{
Logger.Warning?.Print(LogClass.Audio, $"{sampleFormat} isn't supported by the audio device, conversion to {hardwareSampleFormat} will happen.");
if (hardwareSampleFormat < sampleFormat)
{
Logger.Warning?.Print(LogClass.Audio, $"{hardwareSampleFormat} has lower quality than {sampleFormat}, expect some loss in audio fidelity.");
}
}
if (direction == Direction.Input)
{
Logger.Warning?.Print(LogClass.Audio, $"The selected audio backend doesn't support the requested audio input configuration, fallback to dummy...");
@@ -103,7 +148,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
}
// If we need to do post processing before sending to the hardware device, wrap around it.
return new CompatLayerHardwareDeviceSession(realSessionOutputBase, channelCount);
return new CompatLayerHardwareDeviceSession(realSessionOutputBase, sampleFormat, channelCount);
}
public bool SupportsChannelCount(uint channelCount)

View File

@@ -1,5 +1,6 @@
using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Dsp;
using System;
using System.Runtime.InteropServices;
@@ -8,11 +9,13 @@ namespace Ryujinx.Audio.Backends.CompatLayer
class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private HardwareDeviceSessionOutputBase _realSession;
private SampleFormat _userSampleFormat;
private uint _userChannelCount;
public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, SampleFormat userSampleFormat, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
{
_realSession = realSession;
_userSampleFormat = userSampleFormat;
_userChannelCount = userChannelCount;
}
@@ -38,53 +41,86 @@ namespace Ryujinx.Audio.Backends.CompatLayer
public override void QueueBuffer(AudioBuffer buffer)
{
SampleFormat realSampleFormat = _realSession.RequestedSampleFormat;
if (_userSampleFormat != realSampleFormat)
{
if (_userSampleFormat != SampleFormat.PcmInt16)
{
throw new NotImplementedException("Converting formats other than PCM16 is not supported.");
}
int userSampleCount = buffer.Data.Length / BackendHelper.GetSampleSize(_userSampleFormat);
ReadOnlySpan<short> samples = MemoryMarshal.Cast<byte, short>(buffer.Data);
byte[] convertedSamples = new byte[BackendHelper.GetSampleSize(realSampleFormat) * userSampleCount];
switch (realSampleFormat)
{
case SampleFormat.PcmInt8:
PcmHelper.Convert(MemoryMarshal.Cast<byte, sbyte>(convertedSamples), samples);
break;
case SampleFormat.PcmInt32:
PcmHelper.Convert(MemoryMarshal.Cast<byte, int>(convertedSamples), samples);
break;
case SampleFormat.PcmFloat:
PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast<byte, float>(convertedSamples), samples);
break;
default:
throw new NotImplementedException($"Sample format conversion from {_userSampleFormat} to {realSampleFormat} not implemented.");
}
buffer.Data = convertedSamples;
}
_realSession.QueueBuffer(buffer);
}
public override bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
{
if (RequestedSampleFormat != SampleFormat.PcmInt16)
{
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
}
if (samples == null)
{
return false;
}
short[] downmixedBufferPCM16;
if (_userChannelCount != _realSession.RequestedChannelCount)
{
if (_userSampleFormat != SampleFormat.PcmInt16)
{
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
}
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
if (_userChannelCount == 6)
{
downmixedBufferPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
samplesPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
if (_realSession.RequestedChannelCount == 1)
{
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(downmixedBufferPCM16);
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
}
}
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
{
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
}
else
{
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
}
byte[] downmixedBuffer = MemoryMarshal.Cast<short, byte>(downmixedBufferPCM16).ToArray();
samples = MemoryMarshal.Cast<short, byte>(samplesPCM16).ToArray();
}
AudioBuffer fakeBuffer = new AudioBuffer
{
BufferTag = buffer.BufferTag,
DataPointer = buffer.DataPointer,
DataSize = (ulong)downmixedBuffer.Length
DataSize = (ulong)samples.Length
};
bool result = _realSession.RegisterBuffer(fakeBuffer, downmixedBuffer);
bool result = _realSession.RegisterBuffer(fakeBuffer, samples);
if (result)
{

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common
if (size != 0)
{
ulong alignedOffset = BitUtils.AlignUp(Offset, align);
ulong alignedOffset = BitUtils.AlignUp<ulong>(Offset, (ulong)align);
if (alignedOffset + size <= (ulong)BackingMemory.Length)
{
@@ -55,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Common
public static ulong GetTargetSize<T>(ulong currentSize, ulong count, int align) where T : unmanaged
{
return BitUtils.AlignUp(currentSize, align) + (ulong)Unsafe.SizeOf<T>() * count;
return BitUtils.AlignUp<ulong>(currentSize, (ulong)align) + (ulong)Unsafe.SizeOf<T>() * count;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp
@@ -23,6 +24,44 @@ namespace Ryujinx.Audio.Renderer.Dsp
return GetCountToDecode(startSampleOffset, endSampleOffset, offset, count) * Unsafe.SizeOf<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ConvertSampleToPcmFloat(short sample)
{
return (float)sample / short.MaxValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ConvertSampleToPcmInt16(float sample)
{
return Saturate(sample * short.MaxValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TOutput ConvertSample<TInput, TOutput>(TInput value) where TInput: INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
{
TInput conversionRate = TInput.CreateSaturating(TOutput.MaxValue / TOutput.CreateSaturating(TInput.MaxValue));
return TOutput.CreateSaturating(value * conversionRate);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert<TInput, TOutput>(Span<TOutput> output, ReadOnlySpan<TInput> input) where TInput : INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
{
for (int i = 0; i < input.Length; i++)
{
output[i] = ConvertSample<TInput, TOutput>(input[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ConvertSampleToPcmFloat(Span<float> output, ReadOnlySpan<short> input)
{
for (int i = 0; i < input.Length; i++)
{
output[i] = ConvertSampleToPcmFloat(input[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Decode(Span<short> output, ReadOnlySpan<short> input, int startSampleOffset, int endSampleOffset, int channelIndex, int channelCount)
{
@@ -53,7 +92,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
for (int i = 0; i < decodedCount; i++)
{
output[i] = (short)(input[i * channelCount + channelIndex] * short.MaxValue);
output[i] = ConvertSampleToPcmInt16(input[i * channelCount + channelIndex]);
}
return decodedCount;

View File

@@ -167,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall;
}
_depopBuffer = workBufferAllocator.Allocate<float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
_depopBuffer = workBufferAllocator.Allocate<float>(BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
if (_depopBuffer.IsEmpty)
{
@@ -772,7 +772,7 @@ namespace Ryujinx.Audio.Renderer.Server
size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
// Depop buffer
size = WorkBufferAllocator.GetTargetSize<float>(size, (ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
size = WorkBufferAllocator.GetTargetSize<float>(size, BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
// Voice
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment);
@@ -804,10 +804,10 @@ namespace Ryujinx.Audio.Renderer.Server
{
ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
size += BitUtils.AlignUp(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
size += BitUtils.AlignUp<ulong>(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
}
return BitUtils.AlignUp(size, Constants.WorkBufferAlignment);
return BitUtils.AlignUp<ulong>(size, Constants.WorkBufferAlignment);
}
public ResultCode QuerySystemEvent(out IWritableEvent systemEvent)

View File

@@ -24,10 +24,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public int Get(K key, ref V[] overlaps)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
IntervalTreeNode<K, V> node = GetNode(key);
@@ -61,15 +58,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="start"/> or <paramref name="end"/> is null</exception>
public int Get(K start, K end, ref V[] overlaps, int overlapCount = 0)
{
if (start == null)
{
throw new ArgumentNullException(nameof(start));
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
ArgumentNullException.ThrowIfNull(start);
ArgumentNullException.ThrowIfNull(end);
GetValues(Root, start, end, ref overlaps, ref overlapCount);
@@ -85,20 +75,9 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="start"/>, <paramref name="end"/> or <paramref name="value"/> are null</exception>
public void Add(K start, K end, V value)
{
if (start == null)
{
throw new ArgumentNullException(nameof(start));
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(start);
ArgumentNullException.ThrowIfNull(end);
ArgumentNullException.ThrowIfNull(value);
Insert(start, end, value);
}
@@ -112,10 +91,7 @@ namespace Ryujinx.Common.Collections
/// <returns>Number of deleted values</returns>
public int Remove(K key, V value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
int removed = Delete(key, value);
@@ -168,10 +144,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private IntervalTreeNode<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
IntervalTreeNode<K, V> node = Root;
while (node != null)
@@ -462,10 +435,8 @@ namespace Ryujinx.Common.Collections
public bool ContainsKey(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
return GetNode(key) != null;
}
}

View File

@@ -17,10 +17,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
public void Add(T node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
ArgumentNullException.ThrowIfNull(node);
Insert(node);
}
@@ -32,10 +29,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
public void Remove(T node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
ArgumentNullException.ThrowIfNull(node);
if (Delete(node) != null)
{
Count--;
@@ -50,10 +45,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="searchNode"/> is null</exception>
public T GetNode(T searchNode)
{
if (searchNode == null)
{
throw new ArgumentNullException(nameof(searchNode));
}
ArgumentNullException.ThrowIfNull(searchNode);
T node = Root;
while (node != null)

View File

@@ -92,10 +92,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
protected static T Minimum(T node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
ArgumentNullException.ThrowIfNull(node);
T tmp = node;
while (tmp.Left != null)
{

View File

@@ -22,10 +22,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public V Get(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> node = GetNode(key);
@@ -47,14 +44,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> are null</exception>
public void Add(K key, V value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(key);
ArgumentNullException.ThrowIfNull(value);
Insert(key, value);
}
@@ -66,10 +57,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public void Remove(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
if (Delete(key) != null)
{
Count--;
@@ -217,10 +206,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private Node<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> node = Root;
while (node != null)
@@ -370,10 +356,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private Node<K, V> FloorNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> tmp = Root;
while (tmp != null)
@@ -424,10 +408,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private Node<K, V> CeilingNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> tmp = Root;
while (tmp != null)
@@ -477,10 +459,8 @@ namespace Ryujinx.Common.Collections
// Method descriptions are not provided as they are already included as part of the interface.
public bool ContainsKey(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
return GetNode(key) != null;
}
@@ -493,10 +473,8 @@ namespace Ryujinx.Common.Collections
public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value)
{
if (null == key)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> node = GetNode(key);
value = node != null ? node.Value : default;
return node != null;
@@ -504,10 +482,7 @@ namespace Ryujinx.Common.Collections
public void Add(KeyValuePair<K, V> item)
{
if (item.Key == null)
{
throw new ArgumentNullException(nameof(item.Key));
}
ArgumentNullException.ThrowIfNull(item.Key);
Add(item.Key, item.Value);
}

View File

@@ -1,82 +1,26 @@
using System;
using System.Numerics;
namespace Ryujinx.Common
{
public static class BitUtils
{
public static uint AlignUp(uint value, int size)
public static T AlignUp<T>(T value, T size)
where T : IBinaryInteger<T>
{
return (uint)AlignUp((int)value, size);
return (value + (size - T.One)) & -size;
}
public static int AlignUp(int value, int size)
{
return (value + (size - 1)) & -size;
}
public static ulong AlignUp(ulong value, int size)
{
return (ulong)AlignUp((long)value, size);
}
public static long AlignUp(long value, int size)
{
return AlignUp(value, (long)size);
}
public static ulong AlignUp(ulong value, ulong size)
{
return (ulong)AlignUp((long)value, (long)size);
}
public static long AlignUp(long value, long size)
{
return (value + (size - 1)) & -size;
}
public static uint AlignDown(uint value, int size)
{
return (uint)AlignDown((int)value, size);
}
public static int AlignDown(int value, int size)
public static T AlignDown<T>(T value, T size)
where T : IBinaryInteger<T>
{
return value & -size;
}
public static ulong AlignDown(ulong value, int size)
public static T DivRoundUp<T>(T value, T dividend)
where T : IBinaryInteger<T>
{
return (ulong)AlignDown((long)value, size);
}
public static long AlignDown(long value, int size)
{
return AlignDown(value, (long)size);
}
public static ulong AlignDown(ulong value, ulong size)
{
return (ulong)AlignDown((long)value, (long)size);
}
public static long AlignDown(long value, long size)
{
return value & -size;
}
public static int DivRoundUp(int value, int dividend)
{
return (value + dividend - 1) / dividend;
}
public static ulong DivRoundUp(ulong value, uint dividend)
{
return (value + dividend - 1) / dividend;
}
public static long DivRoundUp(long value, int dividend)
{
return (value + dividend - 1) / dividend;
return (value + (dividend - T.One)) / dividend;
}
public static int Pow2RoundUp(int value)

View File

@@ -18,7 +18,9 @@ namespace Ryujinx.Graphics.GAL
public readonly bool Supports3DTextureCompression;
public readonly bool SupportsBgraFormat;
public readonly bool SupportsR4G4Format;
public readonly bool SupportsR4G4B4A4Format;
public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool Supports5BitComponentFormat;
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough;
@@ -55,7 +57,9 @@ namespace Ryujinx.Graphics.GAL
bool supports3DTextureCompression,
bool supportsBgraFormat,
bool supportsR4G4Format,
bool supportsR4G4B4A4Format,
bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat,
bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough,
@@ -89,7 +93,9 @@ namespace Ryujinx.Graphics.GAL
Supports3DTextureCompression = supports3DTextureCompression;
SupportsBgraFormat = supportsBgraFormat;
SupportsR4G4Format = supportsR4G4Format;
SupportsR4G4B4A4Format = supportsR4G4B4A4Format;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;

View File

@@ -448,6 +448,27 @@ namespace Ryujinx.Graphics.GAL
return false;
}
/// <summary>
/// Checks if the texture format is 16 bit packed.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>True if the texture format is 16 bit packed, false otherwise</returns>
public static bool Is16BitPacked(this Format format)
{
switch (format)
{
case Format.B5G6R5Unorm:
case Format.B5G5R5A1Unorm:
case Format.R5G5B5X1Unorm:
case Format.R5G5B5A1Unorm:
case Format.R5G6B5Unorm:
case Format.R4G4B4A4Unorm:
return true;
}
return false;
}
/// <summary>
/// Checks if the texture format is an ASTC format.
/// </summary>

View File

@@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
_isLinear = (argument & 1) != 0;
_offset = 0;
_size = (int)(BitUtils.AlignUp(state.LineLengthIn, 4) * state.LineCount);
_size = (int)(BitUtils.AlignUp<uint>(state.LineLengthIn, 4) * state.LineCount);
int count = _size / 4;

View File

@@ -911,7 +911,40 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
{
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result);
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width);
if (!_context.Capabilities.SupportsR4G4B4A4Format)
{
result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
}
}
else if (Format == Format.R4G4B4A4Unorm)
{
if (!_context.Capabilities.SupportsR4G4B4A4Format)
{
result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
}
}
else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
{
switch (Format)
{
case Format.B5G6R5Unorm:
case Format.R5G6B5Unorm:
result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
break;
case Format.B5G5R5A1Unorm:
case Format.R5G5B5X1Unorm:
case Format.R5G5B5A1Unorm:
result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm);
break;
case Format.A1B5G5R5Unorm:
result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
break;
case Format.R4G4B4A4Unorm:
result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
break;
}
}
return result;

View File

@@ -131,9 +131,28 @@ namespace Ryujinx.Graphics.Gpu.Image
}
if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
{
if (caps.SupportsR4G4B4A4Format)
{
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
else
{
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
}
if (info.FormatInfo.Format == Format.R4G4B4A4Unorm)
{
if (!caps.SupportsR4G4B4A4Format)
{
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
}
else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked())
{
return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
return info.FormatInfo;
}

View File

@@ -252,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
@@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);

View File

@@ -114,7 +114,9 @@ namespace Ryujinx.Graphics.OpenGL
supports3DTextureCompression: false,
supportsBgraFormat: false,
supportsR4G4Format: false,
supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,

View File

@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Texture
{
public static class LayoutConverter
{
private const int HostStrideAlignment = 4;
public const int HostStrideAlignment = 4;
public static void ConvertBlockLinearToLinear(
Span<byte> dst,

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@@ -7,9 +8,27 @@ namespace Ryujinx.Graphics.Texture
{
public static class PixelConverter
{
public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data)
private static (int remainder, int outRemainder, int height) GetLineRemainders(int length, int width, int bpp, int outBpp)
{
int stride = BitUtils.AlignUp(width * bpp, LayoutConverter.HostStrideAlignment);
int remainder = stride / bpp - width;
int outStride = BitUtils.AlignUp(width * outBpp, LayoutConverter.HostStrideAlignment);
int outRemainder = outStride / outBpp - width;
return (remainder, outRemainder, length / stride);
}
public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
if (remainder == 0)
{
int start = 0;
if (Sse41.IsSupported)
@@ -26,12 +45,170 @@ namespace Ryujinx.Graphics.Texture
}
}
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
for (int i = start; i < data.Length; i++)
{
outputSpan[i] = (ushort)data[i];
}
}
else
{
int offset = 0;
int outOffset = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
outputSpan[outOffset++] = data[offset++];
}
offset += remainder;
outOffset += outRemainder;
}
}
return output;
}
public unsafe static byte[] ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint outputPacked = 0xff000000;
outputPacked |= (packed << 3) & 0x000000f8;
outputPacked |= (packed << 8) & 0x00f80000;
// Replicate 5 bit components.
outputPacked |= (outputPacked >> 5) & 0x00070007;
// Include and replicate 6 bit component.
outputPacked |= ((packed << 5) & 0x0000fc00) | ((packed >> 1) & 0x00000300);
outputSpan[outOffset++] = outputPacked;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}
public unsafe static byte[] ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint a = forceAlpha ? 1 : (packed >> 15);
uint outputPacked = a * 0xff000000;
outputPacked |= (packed << 3) & 0x000000f8;
outputPacked |= (packed << 6) & 0x0000f800;
outputPacked |= (packed << 9) & 0x00f80000;
// Replicate 5 bit components.
outputPacked |= (outputPacked >> 5) & 0x00070707;
outputSpan[outOffset++] = outputPacked;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}
public unsafe static byte[] ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint a = packed >> 15;
uint outputPacked = a * 0xff000000;
outputPacked |= (packed >> 8) & 0x000000f8;
outputPacked |= (packed << 5) & 0x0000f800;
outputPacked |= (packed << 18) & 0x00f80000;
// Replicate 5 bit components.
outputPacked |= (outputPacked >> 5) & 0x00070707;
outputSpan[outOffset++] = outputPacked;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}
public unsafe static byte[] ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint outputPacked = packed & 0x0000000f;
outputPacked |= (packed << 4) & 0x00000f00;
outputPacked |= (packed << 8) & 0x000f0000;
outputPacked |= (packed << 12) & 0x0f000000;
outputSpan[outOffset++] = outputPacked * 0x11;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}

View File

@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan
{
var range = _freeRanges[i];
ulong alignedOffset = BitUtils.AlignUp(range.Offset, (int)alignment);
ulong alignedOffset = BitUtils.AlignUp<ulong>(range.Offset, alignment);
ulong sizeDelta = alignedOffset - range.Offset;
ulong usableSize = range.Size - sizeDelta;
@@ -196,7 +196,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
ulong blockAlignedSize = BitUtils.AlignUp(size, _blockAlignment);
ulong blockAlignedSize = BitUtils.AlignUp<ulong>(size, (ulong)_blockAlignment);
var memoryAllocateInfo = new MemoryAllocateInfo()
{

View File

@@ -396,6 +396,17 @@ namespace Ryujinx.Graphics.Vulkan
GAL.Format.Etc2RgbSrgb,
GAL.Format.Etc2RgbUnorm);
bool supports5BitComponentFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.R5G6B5Unorm,
GAL.Format.R5G5B5A1Unorm,
GAL.Format.R5G5B5X1Unorm,
GAL.Format.B5G6R5Unorm,
GAL.Format.B5G5R5A1Unorm,
GAL.Format.A1B5G5R5Unorm);
bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.R4G4B4A4Unorm);
PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
{
SType = StructureType.PhysicalDeviceVulkan12Features
@@ -425,7 +436,9 @@ namespace Ryujinx.Graphics.Vulkan
supports3DTextureCompression: true,
supportsBgraFormat: true,
supportsR4G4Format: false,
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false,
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,

View File

@@ -81,8 +81,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
attributeMask |= MemoryAttribute.DeviceMapped;
}
ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KPageTableBase.PageSize);
ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KPageTableBase.PageSize);
ulong clientAddrTruncated = BitUtils.AlignDown<ulong>(desc.ClientAddress, KPageTableBase.PageSize);
ulong clientAddrRounded = BitUtils.AlignUp<ulong>(desc.ClientAddress, KPageTableBase.PageSize);
// Check if address is not aligned, in this case we need to perform 2 copies.
if (clientAddrTruncated != clientAddrRounded)
@@ -113,9 +113,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ulong clientEndAddr = desc.ClientAddress + desc.Size;
ulong serverEndAddr = desc.ServerAddress + desc.Size;
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KPageTableBase.PageSize);
ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KPageTableBase.PageSize);
ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KPageTableBase.PageSize);
ulong clientEndAddrTruncated = BitUtils.AlignDown<ulong>(clientEndAddr, (ulong)KPageTableBase.PageSize);
ulong clientEndAddrRounded = BitUtils.AlignUp<ulong>(clientEndAddr, KPageTableBase.PageSize);
ulong serverEndAddrTruncated = BitUtils.AlignDown<ulong>(serverEndAddr, (ulong)KPageTableBase.PageSize);
if (clientEndAddrTruncated < clientEndAddrRounded &&
(clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))

View File

@@ -1005,7 +1005,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
recvListEndAddr = recvListBaseAddr + size;
}
recvListBufferAddress = BitUtils.AlignUp(recvListBaseAddr + dstOffset, 0x10);
recvListBufferAddress = BitUtils.AlignUp<ulong>(recvListBaseAddr + dstOffset, 0x10);
ulong endAddress = recvListBufferAddress + descriptor.BufferSize;

View File

@@ -44,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Map(ulong address, ulong size, KMemoryPermission perm)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Unmap(ulong address, ulong size)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult UnmapFromOwner(ulong address, ulong size)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}

View File

@@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int depth = HighestDepthIndex; depth >= 0; depth--)
{
_bitStorages[depth] = storage;
size = BitUtils.DivRoundUp(size, UInt64BitSize);
size = BitUtils.DivRoundUp<ulong>(size, (ulong)UInt64BitSize);
storage = storage.Slice((int)size);
}
@@ -288,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int depth = GetRequiredDepth(regionSize) - 1; depth >= 0; depth--)
{
regionSize = BitUtils.DivRoundUp(regionSize, UInt64BitSize);
regionSize = BitUtils.DivRoundUp<ulong>(regionSize, UInt64BitSize);
overheadBits += (int)regionSize;
}

View File

@@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
int diff = 1 << (NextShift - Shift);
offset = BitUtils.AlignDown(offset, diff);
offset = BitUtils.AlignDown(offset, (ulong)diff);
if (_bitmap.ClearRange(offset, diff))
{

View File

@@ -203,8 +203,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
heapRegion.Size = 0x180000000;
stackRegion.Size = 0x80000000;
tlsIoRegion.Size = 0x1000000000;
CodeRegionStart = BitUtils.AlignDown(address, 0x200000);
codeRegionSize = BitUtils.AlignUp(endAddr, 0x200000) - CodeRegionStart;
CodeRegionStart = BitUtils.AlignDown<ulong>(address, 0x200000);
codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, 0x200000) - CodeRegionStart;
stackAndTlsIoStart = 0;
stackAndTlsIoEnd = 0;
baseAddress = 0x8000000;
@@ -1584,8 +1584,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfResource;
}
ulong srcMapAddress = BitUtils.AlignUp(src, PageSize);
ulong srcMapEndAddr = BitUtils.AlignDown(src + size, PageSize);
ulong srcMapAddress = BitUtils.AlignUp<ulong>(src, PageSize);
ulong srcMapEndAddr = BitUtils.AlignDown<ulong>(src + size, PageSize);
ulong srcMapSize = srcMapEndAddr - srcMapAddress;
result = MapPagesFromClientProcess(size, src, permission, state, srcPageTable, send, out ulong va);
@@ -1659,10 +1659,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
attributeMask |= MemoryAttribute.DeviceMapped;
}
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
{
@@ -1769,10 +1769,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong endAddr = address + size;
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong neededSize = endAddrRounded - addressTruncated;
@@ -1983,10 +1983,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfResource;
}
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
@@ -2010,10 +2010,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
ulong endAddr = address + size;
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
ulong pagesCount = addressRounded < endAddrTruncated ? (endAddrTruncated - addressRounded) / PageSize : 0;
@@ -2540,7 +2540,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int attempt = 0; attempt < 8; attempt++)
{
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, (ulong)alignment);
ulong aslrEndAddr = aslrAddress + totalNeededSize;
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
@@ -2618,7 +2618,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
currBaseAddr += reservedSize;
ulong address = BitUtils.AlignDown(currBaseAddr, alignment) + reservedStart;
ulong address = BitUtils.AlignDown<ulong>(currBaseAddr, (ulong)alignment) + reservedStart;
if (currBaseAddr > address)
{
@@ -2834,7 +2834,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
lock (_blockManager)
{
return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
return BitUtils.DivRoundUp<ulong>(GetMmUsedSize(), PageSize);
}
}

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KProcess process,
KMemoryPermission permission)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}

View File

@@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KProcess process,
KMemoryPermission permission)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong size,
KProcess process)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}

View File

@@ -472,7 +472,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr)
{
ulong tlsPageAddr = BitUtils.AlignDown(tlsSlotAddr, KPageTableBase.PageSize);
ulong tlsPageAddr = BitUtils.AlignDown<ulong>(tlsSlotAddr, KPageTableBase.PageSize);
KernelContext.CriticalSection.Enter();
@@ -554,7 +554,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
throw new InvalidOperationException("Trying to start a process with a invalid state!");
}
ulong stackSizeRounded = BitUtils.AlignUp(stackSize, KPageTableBase.PageSize);
ulong stackSizeRounded = BitUtils.AlignUp<ulong>(stackSize, KPageTableBase.PageSize);
ulong neededSize = stackSizeRounded + _imageSize;

View File

@@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS
endOffset = kip.BssOffset + kip.BssSize;
}
uint codeSize = BitUtils.AlignUp(kip.TextOffset + endOffset, KPageTableBase.PageSize);
uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize);
int codePagesCount = (int)(codeSize / KPageTableBase.PageSize);
@@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS
nsoSize = dataEnd;
}
nsoSize = BitUtils.AlignUp(nsoSize, KPageTableBase.PageSize);
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
nsoBase[index] = codeStart + (ulong)codeSize;
@@ -349,7 +349,7 @@ namespace Ryujinx.HLE.HOS
return KernelResult.Success;
}
size = BitUtils.AlignUp(size, KPageTableBase.PageSize);
size = BitUtils.AlignUp<ulong>(size, KPageTableBase.PageSize);
return process.MemoryManager.SetProcessMemoryPermission(address, size, permission);
}

View File

@@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
ulong bssStart = dataStart + (ulong)relocatableObject.Data.Length;
ulong bssEnd = BitUtils.AlignUp(bssStart + (ulong)relocatableObject.BssSize, KPageTableBase.PageSize);
ulong bssEnd = BitUtils.AlignUp<ulong>(bssStart + relocatableObject.BssSize, KPageTableBase.PageSize);
process.CpuMemory.Write(textStart, relocatableObject.Text);
process.CpuMemory.Write(roStart, relocatableObject.Ro);

View File

@@ -530,8 +530,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
receiveRegion.Dispose();
if (sockAddrOutSize != 0 && sockAddrOutSize >= (ulong) Unsafe.SizeOf<BsdSockAddr>())
{
context.Memory.Write(sockAddrOutPosition, BsdSockAddr.FromIPEndPoint(endPoint));
}
else
{
errno = LinuxError.ENOMEM;
}
}
}
return WriteBsdResult(context, result, errno);

View File

@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
uint headerSize = (uint)Unsafe.SizeOf<ParcelHeader>();
_rawData = new byte[BitUtils.AlignUp(headerSize + payloadSize + objectsSize, 4)];
_rawData = new byte[BitUtils.AlignUp<uint>(headerSize + payloadSize + objectsSize, 4)];
Header.PayloadSize = payloadSize;
Header.ObjectsSize = objectsSize;

View File

@@ -32,20 +32,9 @@ namespace Ryujinx.HLE
public Switch(HLEConfiguration configuration)
{
if (configuration.GpuRenderer == null)
{
throw new ArgumentNullException(nameof(configuration.GpuRenderer));
}
if (configuration.AudioDeviceDriver == null)
{
throw new ArgumentNullException(nameof(configuration.AudioDeviceDriver));
}
if (configuration.UserChannelPersistence == null)
{
throw new ArgumentNullException(nameof(configuration.UserChannelPersistence));
}
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver);
ArgumentNullException.ThrowIfNull(configuration.UserChannelPersistence);
Configuration = configuration;
FileSystem = Configuration.VirtualFileSystem;

View File

@@ -77,7 +77,7 @@ namespace Ryujinx.Headless.SDL2
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient);
_userChannelPersistence = new UserChannelPersistence();
if (OperatingSystem.IsMacOS())
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
{
AutoResetEvent invoked = new AutoResetEvent(false);

View File

@@ -279,10 +279,7 @@ namespace Ryujinx.Memory
{
IntPtr ptr = _pointer;
if (ptr == IntPtr.Zero)
{
ThrowObjectDisposed();
}
ObjectDisposedException.ThrowIf(ptr == IntPtr.Zero, this);
int size = Unsafe.SizeOf<T>();
@@ -312,10 +309,7 @@ namespace Ryujinx.Memory
{
IntPtr ptr = _pointer;
if (ptr == IntPtr.Zero)
{
ThrowObjectDisposed();
}
ObjectDisposedException.ThrowIf(ptr == IntPtr.Zero, this);
ulong endOffset = offset + size;
@@ -454,7 +448,6 @@ namespace Ryujinx.Memory
return true;
}
private static void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock));
private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException();
}
}

View File

@@ -411,10 +411,7 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
public void Dispose()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
ObjectDisposedException.ThrowIf(_disposed, this);
_disposed = true;

7
nuget.config Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>