Compare commits

...

7 Commits

Author SHA1 Message Date
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
Berkan Diler
37d27c4c99 Some minor cleanups and optimizations (#4174)
* Replace Array.Clear(x, 0, x.Length) with Array.Clear(x)

* Use DateTime.UnixEpoch field

* Replace SHA256.ComputeHash calls with static SHA256.HashData call

More performant and avoids the need to initialize a SHA256 instance.
2022-12-24 14:30:39 -03:00
gdkchan
f906eb06c2 Implement a software ETC2 texture decoder (#4121)
* Implement a software ETC2 texture decoder

* Fix output size calculation for non-2D textures

* Address PR feedback
2022-12-21 20:39:58 -03:00
gdkchan
219f63ff4e Fix CPU FCVTN instruction implementation (slow path) (#4159)
* Fix CPU FCVTN instruction implementation (slow path)

* PPTC version bump
2022-12-21 23:05:58 +00:00
52 changed files with 1210 additions and 389 deletions

View File

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

View File

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

View File

@@ -381,7 +381,7 @@ namespace ARMeilleure.Instructions
for (int index = 0; index < elems; index++) for (int index = 0; index < elems; index++)
{ {
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0); Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
if (sizeF == 0) if (sizeF == 0)
{ {
@@ -389,8 +389,6 @@ namespace ARMeilleure.Instructions
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne); Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
context.LoadFromContext(); context.LoadFromContext();
e = context.ZeroExtend16(OperandType.I64, e);
res = EmitVectorInsert(context, res, e, part + index, 1); res = EmitVectorInsert(context, res, e, part + index, 1);
} }
else /* if (sizeF == 1) */ else /* if (sizeF == 1) */

View File

@@ -48,10 +48,7 @@ namespace ARMeilleure.IntermediateRepresentation
public void AddSuccessor(BasicBlock block) public void AddSuccessor(BasicBlock block)
{ {
if (block == null) ArgumentNullException.ThrowIfNull(block);
{
ThrowNull(nameof(block));
}
if ((uint)_succCount + 1 > MaxSuccessors) if ((uint)_succCount + 1 > MaxSuccessors)
{ {
@@ -100,10 +97,7 @@ namespace ARMeilleure.IntermediateRepresentation
public void SetSuccessor(int index, BasicBlock block) public void SetSuccessor(int index, BasicBlock block)
{ {
if (block == null) ArgumentNullException.ThrowIfNull(block);
{
ThrowNull(nameof(block));
}
if ((uint)index >= (uint)_succCount) 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 ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors."); 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) public static Delegate GetDelegate(MethodInfo info)
{ {
if (info == null) ArgumentNullException.ThrowIfNull(info);
{
throw new ArgumentNullException(nameof(info));
}
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray(); Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
Type returnType = info.ReturnType; Type returnType = info.ReturnType;

View File

@@ -35,10 +35,7 @@ namespace ARMeilleure.Translation
public static IntPtr GetDelegateFuncPtr(MethodInfo info) public static IntPtr GetDelegateFuncPtr(MethodInfo info)
{ {
if (info == null) ArgumentNullException.ThrowIfNull(info);
{
throw new ArgumentNullException(nameof(info));
}
string key = GetKey(info); string key = GetKey(info);
@@ -52,10 +49,7 @@ namespace ARMeilleure.Translation
public static int GetDelegateIndex(MethodInfo info) public static int GetDelegateIndex(MethodInfo info)
{ {
if (info == null) ArgumentNullException.ThrowIfNull(info);
{
throw new ArgumentNullException(nameof(info));
}
string key = GetKey(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> /// <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) public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
{ {
if (value == null) ArgumentNullException.ThrowIfNull(value);
{
throw new ArgumentNullException(nameof(value));
}
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node); 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> /// <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) public V GetOrAdd(K start, K end, V value)
{ {
if (value == null) ArgumentNullException.ThrowIfNull(value);
{
throw new ArgumentNullException(nameof(value));
}
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node); BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
return node.Value; return node.Value;
@@ -152,10 +146,7 @@ namespace ARMeilleure.Translation
/// <returns>Node reference in the tree</returns> /// <returns>Node reference in the tree</returns>
private IntervalTreeNode<K, V> GetNode(K key) private IntervalTreeNode<K, V> GetNode(K key)
{ {
if (key == null) ArgumentNullException.ThrowIfNull(key);
{
throw new ArgumentNullException(nameof(key));
}
IntervalTreeNode<K, V> node = _root; IntervalTreeNode<K, V> node = _root;
while (node != null) while (node != null)

View File

@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 4140; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 4159; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";

View File

@@ -140,7 +140,7 @@ namespace ARMeilleure.Translation
} }
} }
Array.Clear(localDefs, 0, localDefs.Length); Array.Clear(localDefs);
} }
} }

View File

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

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common
if (size != 0) if (size != 0)
{ {
ulong alignedOffset = BitUtils.AlignUp(Offset, align); ulong alignedOffset = BitUtils.AlignUp<ulong>(Offset, (ulong)align);
if (alignedOffset + size <= (ulong)BackingMemory.Length) 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 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

@@ -167,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall; 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) 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); size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
// Depop buffer // 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 // Voice
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment); 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; 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) 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> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public int Get(K key, ref V[] overlaps) public int Get(K key, ref V[] overlaps)
{ {
if (key == null) ArgumentNullException.ThrowIfNull(key);
{
throw new ArgumentNullException(nameof(key));
}
IntervalTreeNode<K, V> node = GetNode(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> /// <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) public int Get(K start, K end, ref V[] overlaps, int overlapCount = 0)
{ {
if (start == null) ArgumentNullException.ThrowIfNull(start);
{ ArgumentNullException.ThrowIfNull(end);
throw new ArgumentNullException(nameof(start));
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
GetValues(Root, start, end, ref overlaps, ref overlapCount); 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> /// <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) public void Add(K start, K end, V value)
{ {
if (start == null) ArgumentNullException.ThrowIfNull(start);
{ ArgumentNullException.ThrowIfNull(end);
throw new ArgumentNullException(nameof(start)); ArgumentNullException.ThrowIfNull(value);
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
Insert(start, end, value); Insert(start, end, value);
} }
@@ -112,10 +91,7 @@ namespace Ryujinx.Common.Collections
/// <returns>Number of deleted values</returns> /// <returns>Number of deleted values</returns>
public int Remove(K key, V value) public int Remove(K key, V value)
{ {
if (key == null) ArgumentNullException.ThrowIfNull(key);
{
throw new ArgumentNullException(nameof(key));
}
int removed = Delete(key, value); int removed = Delete(key, value);
@@ -168,10 +144,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private IntervalTreeNode<K, V> GetNode(K key) private IntervalTreeNode<K, V> GetNode(K key)
{ {
if (key == null) ArgumentNullException.ThrowIfNull(key);
{
throw new ArgumentNullException(nameof(key));
}
IntervalTreeNode<K, V> node = Root; IntervalTreeNode<K, V> node = Root;
while (node != null) while (node != null)
@@ -462,10 +435,8 @@ namespace Ryujinx.Common.Collections
public bool ContainsKey(K key) public bool ContainsKey(K key)
{ {
if (key == null) ArgumentNullException.ThrowIfNull(key);
{
throw new ArgumentNullException(nameof(key));
}
return GetNode(key) != null; return GetNode(key) != null;
} }
} }

View File

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

View File

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

View File

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

View File

@@ -1,82 +1,26 @@
using System;
using System.Numerics; using System.Numerics;
namespace Ryujinx.Common namespace Ryujinx.Common
{ {
public static class BitUtils 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) public static T AlignDown<T>(T value, T size)
{ where T : IBinaryInteger<T>
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)
{ {
return value & -size; 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); return (value + (dividend - T.One)) / dividend;
}
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;
} }
public static int Pow2RoundUp(int value) public static int Pow2RoundUp(int value)

View File

@@ -14,10 +14,13 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsBc123Compression; public readonly bool SupportsBc123Compression;
public readonly bool SupportsBc45Compression; public readonly bool SupportsBc45Compression;
public readonly bool SupportsBc67Compression; public readonly bool SupportsBc67Compression;
public readonly bool SupportsEtc2Compression;
public readonly bool Supports3DTextureCompression; public readonly bool Supports3DTextureCompression;
public readonly bool SupportsBgraFormat; public readonly bool SupportsBgraFormat;
public readonly bool SupportsR4G4Format; public readonly bool SupportsR4G4Format;
public readonly bool SupportsR4G4B4A4Format;
public readonly bool SupportsSnormBufferTextureFormat; public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool Supports5BitComponentFormat;
public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsGeometryShaderPassthrough;
@@ -50,10 +53,13 @@ namespace Ryujinx.Graphics.GAL
bool supportsBc123Compression, bool supportsBc123Compression,
bool supportsBc45Compression, bool supportsBc45Compression,
bool supportsBc67Compression, bool supportsBc67Compression,
bool supportsEtc2Compression,
bool supports3DTextureCompression, bool supports3DTextureCompression,
bool supportsBgraFormat, bool supportsBgraFormat,
bool supportsR4G4Format, bool supportsR4G4Format,
bool supportsR4G4B4A4Format,
bool supportsSnormBufferTextureFormat, bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat,
bool supportsFragmentShaderInterlock, bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel, bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough, bool supportsGeometryShaderPassthrough,
@@ -83,10 +89,13 @@ namespace Ryujinx.Graphics.GAL
SupportsBc123Compression = supportsBc123Compression; SupportsBc123Compression = supportsBc123Compression;
SupportsBc45Compression = supportsBc45Compression; SupportsBc45Compression = supportsBc45Compression;
SupportsBc67Compression = supportsBc67Compression; SupportsBc67Compression = supportsBc67Compression;
SupportsEtc2Compression = supportsEtc2Compression;
Supports3DTextureCompression = supports3DTextureCompression; Supports3DTextureCompression = supports3DTextureCompression;
SupportsBgraFormat = supportsBgraFormat; SupportsBgraFormat = supportsBgraFormat;
SupportsR4G4Format = supportsR4G4Format; SupportsR4G4Format = supportsR4G4Format;
SupportsR4G4B4A4Format = supportsR4G4B4A4Format;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;

View File

@@ -448,6 +448,27 @@ namespace Ryujinx.Graphics.GAL
return false; 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> /// <summary>
/// Checks if the texture format is an ASTC format. /// Checks if the texture format is an ASTC format.
/// </summary> /// </summary>
@@ -516,6 +537,27 @@ namespace Ryujinx.Graphics.GAL
return false; return false;
} }
/// <summary>
/// Checks if the texture format is an ETC2 format.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>True if the texture format is an ETC2 format, false otherwise</returns>
public static bool IsEtc2(this Format format)
{
switch (format)
{
case Format.Etc2RgbaSrgb:
case Format.Etc2RgbaUnorm:
case Format.Etc2RgbPtaSrgb:
case Format.Etc2RgbPtaUnorm:
case Format.Etc2RgbSrgb:
case Format.Etc2RgbUnorm:
return true;
}
return false;
}
/// <summary> /// <summary>
/// Checks if the texture format is a BGR format. /// Checks if the texture format is a BGR format.
/// </summary> /// </summary>

View File

@@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
_isLinear = (argument & 1) != 0; _isLinear = (argument & 1) != 0;
_offset = 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; int count = _size / 4;

View File

@@ -208,8 +208,10 @@ namespace Ryujinx.Graphics.Gpu.Image
Bc6HSf16Float = Bc6HSf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff90 Bc6HSf16Float = Bc6HSf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff90
Bc6HUf16Float = Bc6HUf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff91 Bc6HUf16Float = Bc6HUf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff91
Etc2RgbUnorm = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24906 Etc2RgbUnorm = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24906
Etc2RgbPtaUnorm = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490a
Etc2RgbaUnorm = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490b Etc2RgbaUnorm = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490b
Etc2RgbUnormSrgb = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4906 Etc2RgbUnormSrgb = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4906
Etc2RgbPtaUnormSrgb = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490a
Etc2RgbaUnormSrgb = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490b Etc2RgbaUnormSrgb = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490b
Astc2D4x4Unorm = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24940 Astc2D4x4Unorm = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24940
Astc2D5x4Unorm = Astc2D5x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24950 Astc2D5x4Unorm = Astc2D5x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24950
@@ -429,8 +431,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{ TextureFormat.Bc6HSf16Float, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16, 4) }, { TextureFormat.Bc6HSf16Float, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16, 4) },
{ TextureFormat.Bc6HUf16Float, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16, 4) }, { TextureFormat.Bc6HUf16Float, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16, 4) },
{ TextureFormat.Etc2RgbUnorm, new FormatInfo(Format.Etc2RgbUnorm, 4, 4, 8, 3) }, { TextureFormat.Etc2RgbUnorm, new FormatInfo(Format.Etc2RgbUnorm, 4, 4, 8, 3) },
{ TextureFormat.Etc2RgbPtaUnorm, new FormatInfo(Format.Etc2RgbPtaUnorm, 4, 4, 8, 4) },
{ TextureFormat.Etc2RgbaUnorm, new FormatInfo(Format.Etc2RgbaUnorm, 4, 4, 16, 4) }, { TextureFormat.Etc2RgbaUnorm, new FormatInfo(Format.Etc2RgbaUnorm, 4, 4, 16, 4) },
{ TextureFormat.Etc2RgbUnormSrgb, new FormatInfo(Format.Etc2RgbSrgb, 4, 4, 8, 3) }, { TextureFormat.Etc2RgbUnormSrgb, new FormatInfo(Format.Etc2RgbSrgb, 4, 4, 8, 3) },
{ TextureFormat.Etc2RgbPtaUnormSrgb, new FormatInfo(Format.Etc2RgbPtaSrgb, 4, 4, 8, 4) },
{ TextureFormat.Etc2RgbaUnormSrgb, new FormatInfo(Format.Etc2RgbaSrgb, 4, 4, 16, 4) }, { TextureFormat.Etc2RgbaUnormSrgb, new FormatInfo(Format.Etc2RgbaSrgb, 4, 4, 16, 4) },
{ TextureFormat.Astc2D4x4Unorm, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) }, { TextureFormat.Astc2D4x4Unorm, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) },
{ TextureFormat.Astc2D5x4Unorm, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16, 4) }, { TextureFormat.Astc2D5x4Unorm, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16, 4) },

View File

@@ -857,9 +857,23 @@ namespace Ryujinx.Graphics.Gpu.Image
result = decoded; result = decoded;
} }
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm) else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
{ {
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result); switch (Format)
{
case Format.Etc2RgbaSrgb:
case Format.Etc2RgbaUnorm:
result = ETC2Decoder.DecodeRgba(result, width, height, depth, levels, layers);
break;
case Format.Etc2RgbPtaSrgb:
case Format.Etc2RgbPtaUnorm:
result = ETC2Decoder.DecodePta(result, width, height, depth, levels, layers);
break;
case Format.Etc2RgbSrgb:
case Format.Etc2RgbUnorm:
result = ETC2Decoder.DecodeRgb(result, width, height, depth, levels, layers);
break;
}
} }
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities)) else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
{ {
@@ -895,6 +909,43 @@ namespace Ryujinx.Graphics.Gpu.Image
break; break;
} }
} }
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
{
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; return result;
} }

View File

@@ -67,6 +67,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A host compatible format</returns> /// <returns>A host compatible format</returns>
public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps) public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps)
{ {
// The host API does not support those compressed formats.
// We assume software decompression will be done for those textures,
// and so we adjust the format here to match the decompressor output.
if (!caps.SupportsAstcCompression) if (!caps.SupportsAstcCompression)
{ {
if (info.FormatInfo.Format.IsAstcUnorm()) if (info.FormatInfo.Format.IsAstcUnorm())
@@ -83,16 +87,8 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
{
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps)) if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
{ {
// The host API does not this compressed format.
// We assume software decompression will be done for those textures,
// and so we adjust the format here to match the decompressor output.
switch (info.FormatInfo.Format) switch (info.FormatInfo.Format)
{ {
case Format.Bc1RgbaSrgb: case Format.Bc1RgbaSrgb:
@@ -119,6 +115,45 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
if (!caps.SupportsEtc2Compression)
{
switch (info.FormatInfo.Format)
{
case Format.Etc2RgbaSrgb:
case Format.Etc2RgbPtaSrgb:
case Format.Etc2RgbSrgb:
return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
case Format.Etc2RgbaUnorm:
case Format.Etc2RgbPtaUnorm:
case Format.Etc2RgbUnorm:
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
}
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; return info.FormatInfo;
} }

View File

@@ -252,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa); 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); ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
@@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa); 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); ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);

View File

@@ -110,10 +110,13 @@ namespace Ryujinx.Graphics.OpenGL
supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc, supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc,
supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc, supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc,
supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension. supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension.
supportsEtc2Compression: true,
supports3DTextureCompression: false, supports3DTextureCompression: false,
supportsBgraFormat: false, supportsBgraFormat: false,
supportsR4G4Format: false, supportsR4G4Format: false,
supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false, supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,

View File

@@ -0,0 +1,682 @@
using Ryujinx.Common;
using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Texture
{
public static class ETC2Decoder
{
private const uint AlphaMask = 0xff000000u;
private const int BlockWidth = 4;
private const int BlockHeight = 4;
private static readonly int[][] _etc1Lut =
{
new int[] { 2, 8, -2, -8 },
new int[] { 5, 17, -5, -17 },
new int[] { 9, 29, -9, -29 },
new int[] { 13, 42, -13, -42 },
new int[] { 18, 60, -18, -60 },
new int[] { 24, 80, -24, -80 },
new int[] { 33, 106, -33, -106 },
new int[] { 47, 183, -47, -183 }
};
private static readonly int[] _etc2Lut =
{
3, 6, 11, 16, 23, 32, 41, 64
};
private static readonly int[][] _etc2AlphaLut =
{
new int[] { -3, -6, -9, -15, 2, 5, 8, 14 },
new int[] { -3, -7, -10, -13, 2, 6, 9, 12 },
new int[] { -2, -5, -8, -13, 1, 4, 7, 12 },
new int[] { -2, -4, -6, -13, 1, 3, 5, 12 },
new int[] { -3, -6, -8, -12, 2, 5, 7, 11 },
new int[] { -3, -7, -9, -11, 2, 6, 8, 10 },
new int[] { -4, -7, -8, -11, 3, 6, 7, 10 },
new int[] { -3, -5, -8, -11, 2, 4, 7, 10 },
new int[] { -2, -6, -8, -10, 1, 5, 7, 9 },
new int[] { -2, -5, -8, -10, 1, 4, 7, 9 },
new int[] { -2, -4, -8, -10, 1, 3, 7, 9 },
new int[] { -2, -5, -7, -10, 1, 4, 6, 9 },
new int[] { -3, -4, -7, -10, 2, 3, 6, 9 },
new int[] { -1, -2, -3, -10, 0, 1, 2, 9 },
new int[] { -4, -6, -8, -9, 3, 5, 7, 8 },
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 }
};
public static byte[] DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
for (int l = 0; l < levels; l++)
{
int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < hInBlocks; y++)
{
int ty = y * BlockHeight;
int bh = Math.Min(BlockHeight, height - ty);
for (int x = 0; x < wInBlocks; x++)
{
int tx = x * BlockWidth;
int bw = Math.Min(BlockWidth, width - tx);
ulong colorBlock = dataUlong[inputOffset++];
DecodeBlock(tile, colorBlock);
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
for (int px = 0; px < bw; px++)
{
int oOffs = oOffsBase + px;
outputUint[oOffs] = tile[py * BlockWidth + px] | AlphaMask;
}
}
}
}
imageBaseOOffs += width * height;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
public static byte[] DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
for (int l = 0; l < levels; l++)
{
int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < hInBlocks; y++)
{
int ty = y * BlockHeight;
int bh = Math.Min(BlockHeight, height - ty);
for (int x = 0; x < wInBlocks; x++)
{
int tx = x * BlockWidth;
int bw = Math.Min(BlockWidth, width - tx);
ulong colorBlock = dataUlong[inputOffset++];
DecodeBlockPta(tile, colorBlock);
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
tile.Slice(py * BlockWidth, bw).CopyTo(outputUint.Slice(oOffsBase, bw));
}
}
}
imageBaseOOffs += width * height;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
public static byte[] DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
for (int l = 0; l < levels; l++)
{
int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < hInBlocks; y++)
{
int ty = y * BlockHeight;
int bh = Math.Min(BlockHeight, height - ty);
for (int x = 0; x < wInBlocks; x++)
{
int tx = x * BlockWidth;
int bw = Math.Min(BlockWidth, width - tx);
ulong alphaBlock = dataUlong[inputOffset];
ulong colorBlock = dataUlong[inputOffset + 1];
inputOffset += 2;
DecodeBlock(tile, colorBlock);
byte alphaBase = (byte)alphaBlock;
int[] alphaTable = _etc2AlphaLut[(alphaBlock >> 8) & 0xf];
int alphaMultiplier = (int)(alphaBlock >> 12) & 0xf;
ulong alphaIndices = BinaryPrimitives.ReverseEndianness(alphaBlock);
if (alphaMultiplier != 0)
{
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
for (int px = 0; px < bw; px++)
{
int oOffs = oOffsBase + px;
int alphaIndex = (int)((alphaIndices >> (((px * BlockHeight + py) ^ 0xf) * 3)) & 7);
byte a = Saturate(alphaBase + alphaTable[alphaIndex] * alphaMultiplier);
outputUint[oOffs] = tile[py * BlockWidth + px] | ((uint)a << 24);
}
}
}
else
{
uint a = (uint)alphaBase << 24;
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
for (int px = 0; px < bw; px++)
{
int oOffs = oOffsBase + px;
outputUint[oOffs] = tile[py * BlockWidth + px] | a;
}
}
}
}
}
imageBaseOOffs += width * height;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
private static void DecodeBlock(Span<uint> tile, ulong block)
{
uint blockLow = (uint)(block >> 0);
uint blockHigh = (uint)(block >> 32);
uint r1, g1, b1;
uint r2, g2, b2;
bool differentialMode = (blockLow & 0x2000000) != 0;
if (differentialMode)
{
(r1, g1, b1, r2, g2, b2) = UnpackRgb555DiffEndPoints(blockLow);
if (r2 > 31)
{
DecodeBlock59T(tile, blockLow, blockHigh);
}
else if (g2 > 31)
{
DecodeBlock58H(tile, blockLow, blockHigh);
}
else if (b2 > 31)
{
DecodeBlock57P(tile, block);
}
else
{
r1 |= r1 >> 5;
g1 |= g1 >> 5;
b1 |= b1 >> 5;
r2 = (r2 << 3) | (r2 >> 2);
g2 = (g2 << 3) | (g2 >> 2);
b2 = (b2 << 3) | (b2 >> 2);
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
}
}
else
{
r1 = (blockLow & 0x0000f0) >> 0;
g1 = (blockLow & 0x00f000) >> 8;
b1 = (blockLow & 0xf00000) >> 16;
r2 = (blockLow & 0x00000f) << 4;
g2 = (blockLow & 0x000f00) >> 4;
b2 = (blockLow & 0x0f0000) >> 12;
r1 |= r1 >> 4;
g1 |= g1 >> 4;
b1 |= b1 >> 4;
r2 |= r2 >> 4;
g2 |= g2 >> 4;
b2 |= b2 >> 4;
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
}
}
private static void DecodeBlockPta(Span<uint> tile, ulong block)
{
uint blockLow = (uint)(block >> 0);
uint blockHigh = (uint)(block >> 32);
(uint r1, uint g1, uint b1, uint r2, uint g2, uint b2) = UnpackRgb555DiffEndPoints(blockLow);
bool fullyOpaque = (blockLow & 0x2000000) != 0;
if (fullyOpaque)
{
if (r2 > 31)
{
DecodeBlock59T(tile, blockLow, blockHigh);
}
else if (g2 > 31)
{
DecodeBlock58H(tile, blockLow, blockHigh);
}
else if (b2 > 31)
{
DecodeBlock57P(tile, block);
}
else
{
r1 |= r1 >> 5;
g1 |= g1 >> 5;
b1 |= b1 >> 5;
r2 = (r2 << 3) | (r2 >> 2);
g2 = (g2 << 3) | (g2 >> 2);
b2 = (b2 << 3) | (b2 >> 2);
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
}
for (int i = 0; i < tile.Length; i++)
{
tile[i] |= AlphaMask;
}
}
else
{
if (r2 > 31)
{
DecodeBlock59T(tile, blockLow, blockHigh, AlphaMask);
}
else if (g2 > 31)
{
DecodeBlock58H(tile, blockLow, blockHigh, AlphaMask);
}
else if (b2 > 31)
{
DecodeBlock57P(tile, block);
for (int i = 0; i < tile.Length; i++)
{
tile[i] |= AlphaMask;
}
}
else
{
r1 |= r1 >> 5;
g1 |= g1 >> 5;
b1 |= b1 >> 5;
r2 = (r2 << 3) | (r2 >> 2);
g2 = (g2 << 3) | (g2 >> 2);
b2 = (b2 << 3) | (b2 >> 2);
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2, AlphaMask);
}
}
}
private static (uint, uint, uint, uint, uint, uint) UnpackRgb555DiffEndPoints(uint blockLow)
{
uint r1 = (blockLow & 0x0000f8) >> 0;
uint g1 = (blockLow & 0x00f800) >> 8;
uint b1 = (blockLow & 0xf80000) >> 16;
uint r2 = (uint)((sbyte)(r1 >> 3) + ((sbyte)((blockLow & 0x000007) << 5) >> 5));
uint g2 = (uint)((sbyte)(g1 >> 3) + ((sbyte)((blockLow & 0x000700) >> 3) >> 5));
uint b2 = (uint)((sbyte)(b1 >> 3) + ((sbyte)((blockLow & 0x070000) >> 11) >> 5));
return (r1, g1, b1, r2, g2, b2);
}
private static void DecodeBlock59T(Span<uint> tile, uint blockLow, uint blockHigh, uint alphaMask = 0)
{
uint r1 = (blockLow & 3) | ((blockLow >> 1) & 0xc);
uint g1 = (blockLow >> 12) & 0xf;
uint b1 = (blockLow >> 8) & 0xf;
uint r2 = (blockLow >> 20) & 0xf;
uint g2 = (blockLow >> 16) & 0xf;
uint b2 = (blockLow >> 28) & 0xf;
r1 |= r1 << 4;
g1 |= g1 << 4;
b1 |= b1 << 4;
r2 |= r2 << 4;
g2 |= g2 << 4;
b2 |= b2 << 4;
int dist = _etc2Lut[((blockLow >> 24) & 1) | ((blockLow >> 25) & 6)];
Span<uint> palette = stackalloc uint[4];
palette[0] = Pack(r1, g1, b1);
palette[1] = Pack(r2, g2, b2, dist);
palette[2] = Pack(r2, g2, b2);
palette[3] = Pack(r2, g2, b2, -dist);
blockHigh = BinaryPrimitives.ReverseEndianness(blockHigh);
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
int offset = (y * 4) + x;
int index = (x * 4) + y;
int paletteIndex = (int)((blockHigh >> index) & 1) | (int)((blockHigh >> (index + 15)) & 2);
tile[offset] = palette[paletteIndex];
if (alphaMask != 0)
{
if (paletteIndex == 2)
{
tile[offset] = 0;
}
else
{
tile[offset] |= alphaMask;
}
}
}
}
}
private static void DecodeBlock58H(Span<uint> tile, uint blockLow, uint blockHigh, uint alphaMask = 0)
{
uint r1 = (blockLow >> 3) & 0xf;
uint g1 = ((blockLow << 1) & 0xe) | ((blockLow >> 12) & 1);
uint b1 = ((blockLow >> 23) & 1) | ((blockLow >> 7) & 6) | ((blockLow >> 8) & 8);
uint r2 = (blockLow >> 19) & 0xf;
uint g2 = ((blockLow >> 31) & 1) | ((blockLow >> 15) & 0xe);
uint b2 = (blockLow >> 27) & 0xf;
uint rgb1 = Pack4Be(r1, g1, b1);
uint rgb2 = Pack4Be(r2, g2, b2);
r1 |= r1 << 4;
g1 |= g1 << 4;
b1 |= b1 << 4;
r2 |= r2 << 4;
g2 |= g2 << 4;
b2 |= b2 << 4;
int dist = _etc2Lut[(rgb1 >= rgb2 ? 1u : 0u) | ((blockLow >> 23) & 2) | ((blockLow >> 24) & 4)];
Span<uint> palette = stackalloc uint[4];
palette[0] = Pack(r1, g1, b1, dist);
palette[1] = Pack(r1, g1, b1, -dist);
palette[2] = Pack(r2, g2, b2, dist);
palette[3] = Pack(r2, g2, b2, -dist);
blockHigh = BinaryPrimitives.ReverseEndianness(blockHigh);
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
int offset = (y * 4) + x;
int index = (x * 4) + y;
int paletteIndex = (int)((blockHigh >> index) & 1) | (int)((blockHigh >> (index + 15)) & 2);
tile[offset] = palette[paletteIndex];
if (alphaMask != 0)
{
if (paletteIndex == 2)
{
tile[offset] = 0;
}
else
{
tile[offset] |= alphaMask;
}
}
}
}
}
private static void DecodeBlock57P(Span<uint> tile, ulong block)
{
int r0 = (int)((block >> 1) & 0x3f);
int g0 = (int)(((block >> 9) & 0x3f) | ((block & 1) << 6));
int b0 = (int)(((block >> 31) & 1) | ((block >> 15) & 6) | ((block >> 16) & 0x18) | ((block >> 3) & 0x20));
int rh = (int)(((block >> 24) & 1) | ((block >> 25) & 0x3e));
int gh = (int)((block >> 33) & 0x7f);
int bh = (int)(((block >> 43) & 0x1f) | ((block >> 27) & 0x20));
int rv = (int)(((block >> 53) & 7) | ((block >> 37) & 0x38));
int gv = (int)(((block >> 62) & 3) | ((block >> 46) & 0x7c));
int bv = (int)((block >> 56) & 0x3f);
r0 = (r0 << 2) | (r0 >> 4);
g0 = (g0 << 1) | (g0 >> 6);
b0 = (b0 << 2) | (b0 >> 4);
rh = (rh << 2) | (rh >> 4);
gh = (gh << 1) | (gh >> 6);
bh = (bh << 2) | (bh >> 4);
rv = (rv << 2) | (rv >> 4);
gv = (gv << 1) | (gv >> 6);
bv = (bv << 2) | (bv >> 4);
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
int offset = y * BlockWidth + x;
byte r = Saturate(((x * (rh - r0)) + (y * (rv - r0)) + (r0 * 4) + 2) >> 2);
byte g = Saturate(((x * (gh - g0)) + (y * (gv - g0)) + (g0 * 4) + 2) >> 2);
byte b = Saturate(((x * (bh - b0)) + (y * (bv - b0)) + (b0 * 4) + 2) >> 2);
tile[offset] = Pack(r, g, b);
}
}
}
private static void DecodeBlockETC1(
Span<uint> tile,
uint blockLow,
uint blockHigh,
uint r1,
uint g1,
uint b1,
uint r2,
uint g2,
uint b2,
uint alphaMask = 0)
{
int[] table1 = _etc1Lut[(blockLow >> 29) & 7];
int[] table2 = _etc1Lut[(blockLow >> 26) & 7];
bool flip = (blockLow & 0x1000000) != 0;
if (!flip)
{
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth / 2; x++)
{
uint color1 = CalculatePixel(r1, g1, b1, x + 0, y, blockHigh, table1, alphaMask);
uint color2 = CalculatePixel(r2, g2, b2, x + 2, y, blockHigh, table2, alphaMask);
int offset1 = y * BlockWidth + x;
int offset2 = y * BlockWidth + x + 2;
tile[offset1] = color1;
tile[offset2] = color2;
}
}
}
else
{
for (int y = 0; y < BlockHeight / 2; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
uint color1 = CalculatePixel(r1, g1, b1, x, y + 0, blockHigh, table1, alphaMask);
uint color2 = CalculatePixel(r2, g2, b2, x, y + 2, blockHigh, table2, alphaMask);
int offset1 = (y * BlockWidth) + x;
int offset2 = ((y + 2) * BlockWidth) + x;
tile[offset1] = color1;
tile[offset2] = color2;
}
}
}
}
private static uint CalculatePixel(uint r, uint g, uint b, int x, int y, uint block, int[] table, uint alphaMask)
{
int index = x * BlockHeight + y;
uint msb = block << 1;
uint tableIndex = index < 8
? ((block >> (index + 24)) & 1) + ((msb >> (index + 8)) & 2)
: ((block >> (index + 8)) & 1) + ((msb >> (index - 8)) & 2);
if (alphaMask != 0)
{
if (tableIndex == 0)
{
return Pack(r, g, b) | alphaMask;
}
else if (tableIndex == 2)
{
return 0;
}
else
{
return Pack(r, g, b, table[tableIndex]) | alphaMask;
}
}
return Pack(r, g, b, table[tableIndex]);
}
private static uint Pack(uint r, uint g, uint b, int offset)
{
r = Saturate((int)(r + offset));
g = Saturate((int)(g + offset));
b = Saturate((int)(b + offset));
return Pack(r, g, b);
}
private static uint Pack(uint r, uint g, uint b)
{
return r | (g << 8) | (b << 16);
}
private static uint Pack4Be(uint r, uint g, uint b)
{
return (r << 8) | (g << 4) | b;
}
private static byte Saturate(int value)
{
return value > byte.MaxValue ? byte.MaxValue : value < byte.MinValue ? byte.MinValue : (byte)value;
}
private static int CalculateOutputSize(int width, int height, int depth, int levels, int layers)
{
int size = 0;
for (int l = 0; l < levels; l++)
{
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
return size;
}
}
}

View File

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

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
@@ -7,30 +8,206 @@ namespace Ryujinx.Graphics.Texture
{ {
public static class PixelConverter 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]; byte[] output = new byte[data.Length * 2];
int start = 0;
if (Sse41.IsSupported) (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
{
int sizeTrunc = data.Length & ~7;
start = sizeTrunc;
fixed (byte* inputPtr = data, outputPtr = output)
{
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
{
Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
}
}
}
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output); Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
for (int i = start; i < data.Length; i++) if (remainder == 0)
{ {
outputSpan[i] = (ushort)data[i]; int start = 0;
if (Sse41.IsSupported)
{
int sizeTrunc = data.Length & ~7;
start = sizeTrunc;
fixed (byte* inputPtr = data, outputPtr = output)
{
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
{
Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
}
}
}
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; return output;

View File

@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
var range = _freeRanges[i]; 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 sizeDelta = alignedOffset - range.Offset;
ulong usableSize = range.Size - sizeDelta; 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() var memoryAllocateInfo = new MemoryAllocateInfo()
{ {

View File

@@ -388,6 +388,24 @@ namespace Ryujinx.Graphics.Vulkan
GAL.Format.Bc7Srgb, GAL.Format.Bc7Srgb,
GAL.Format.Bc7Unorm); GAL.Format.Bc7Unorm);
bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.Etc2RgbaSrgb,
GAL.Format.Etc2RgbaUnorm,
GAL.Format.Etc2RgbPtaSrgb,
GAL.Format.Etc2RgbPtaUnorm,
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() PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
{ {
@@ -414,10 +432,13 @@ namespace Ryujinx.Graphics.Vulkan
supportsBc123Compression: supportsBc123CompressionFormat, supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat, supportsBc45Compression: supportsBc45CompressionFormat,
supportsBc67Compression: supportsBc67CompressionFormat, supportsBc67Compression: supportsBc67CompressionFormat,
supportsEtc2Compression: supportsEtc2CompressionFormat,
supports3DTextureCompression: true, supports3DTextureCompression: true,
supportsBgraFormat: true, supportsBgraFormat: true,
supportsR4G4Format: false, supportsR4G4Format: false,
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true, supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false, supportsFragmentShaderOrderingIntel: false,
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,

View File

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

View File

@@ -1005,7 +1005,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
recvListEndAddr = recvListBaseAddr + size; recvListEndAddr = recvListBaseAddr + size;
} }
recvListBufferAddress = BitUtils.AlignUp(recvListBaseAddr + dstOffset, 0x10); recvListBufferAddress = BitUtils.AlignUp<ulong>(recvListBaseAddr + dstOffset, 0x10);
ulong endAddress = recvListBufferAddress + descriptor.BufferSize; 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) 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; return KernelResult.InvalidSize;
} }
@@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission) 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; return KernelResult.InvalidSize;
} }
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Unmap(ulong address, ulong size) 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; return KernelResult.InvalidSize;
} }
@@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult UnmapFromOwner(ulong address, ulong size) 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; return KernelResult.InvalidSize;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -472,7 +472,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr) public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr)
{ {
ulong tlsPageAddr = BitUtils.AlignDown(tlsSlotAddr, KPageTableBase.PageSize); ulong tlsPageAddr = BitUtils.AlignDown<ulong>(tlsSlotAddr, KPageTableBase.PageSize);
KernelContext.CriticalSection.Enter(); 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!"); 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; ulong neededSize = stackSizeRounded + _imageSize;

View File

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

View File

@@ -4,8 +4,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
public class UserProfile public class UserProfile
{ {
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public UserId UserId { get; } public UserId UserId { get; }
public long LastModifiedTimestamp { get; set; } public long LastModifiedTimestamp { get; set; }
@@ -83,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
private void UpdateLastModifiedTimestamp() private void UpdateLastModifiedTimestamp()
{ {
LastModifiedTimestamp = (long)(DateTime.Now - Epoch).TotalSeconds; LastModifiedTimestamp = (long)(DateTime.Now - DateTime.UnixEpoch).TotalSeconds;
} }
} }
} }

View File

@@ -100,27 +100,24 @@ namespace Ryujinx.HLE.HOS.Services.Caps
Unknown0x1f = 1 Unknown0x1f = 1
}; };
using (SHA256 sha256Hash = SHA256.Create()) // NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = BitConverter.ToString(SHA256.HashData(BitConverter.GetBytes(titleId))).Replace("-", "").Remove(0x20);
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{ {
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead. applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
string hash = BitConverter.ToString(sha256Hash.ComputeHash(BitConverter.GetBytes(titleId))).Replace("-", "").Remove(0x20);
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions. filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
Image.LoadPixelData<Rgba32>(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
} }
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
Image.LoadPixelData<Rgba32>(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
return ResultCode.Success; return ResultCode.Success;
} }

View File

@@ -24,9 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Bpc
public static ResultCode GetExternalRtcValue(out ulong rtcValue) public static ResultCode GetExternalRtcValue(out ulong rtcValue)
{ {
// TODO: emulate MAX77620/MAX77812 RTC // TODO: emulate MAX77620/MAX77812 RTC
DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); rtcValue = (ulong)(DateTime.Now.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds;
rtcValue = (ulong)(DateTime.Now.ToUniversalTime() - unixEpoch).TotalSeconds;
return ResultCode.Success; return ResultCode.Success;
} }

View File

@@ -142,14 +142,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_owner.CpuMemory.Read(nroAddress, nroData); _owner.CpuMemory.Read(nroAddress, nroData);
byte[] nroHash = null;
MemoryStream stream = new MemoryStream(nroData); MemoryStream stream = new MemoryStream(nroData);
using (SHA256 hasher = SHA256.Create()) byte[] nroHash = SHA256.HashData(stream);
{
nroHash = hasher.ComputeHash(stream);
}
if (!IsNroHashPresent(nroHash)) if (!IsNroHashPresent(nroHash))
{ {
@@ -326,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
ulong bssStart = dataStart + (ulong)relocatableObject.Data.Length; 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(textStart, relocatableObject.Text);
process.CpuMemory.Write(roStart, relocatableObject.Ro); process.CpuMemory.Write(roStart, relocatableObject.Ro);

View File

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

View File

@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {
uint headerSize = (uint)Unsafe.SizeOf<ParcelHeader>(); 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.PayloadSize = payloadSize;
Header.ObjectsSize = objectsSize; Header.ObjectsSize = objectsSize;

View File

@@ -10,8 +10,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public static readonly TimeSpanType Zero = new TimeSpanType(0); public static readonly TimeSpanType Zero = new TimeSpanType(0);
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public long NanoSeconds; public long NanoSeconds;
public TimeSpanType(long nanoSeconds) public TimeSpanType(long nanoSeconds)
@@ -31,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public bool IsDaylightSavingTime() public bool IsDaylightSavingTime()
{ {
return UnixEpoch.AddSeconds(ToSeconds()).ToLocalTime().IsDaylightSavingTime(); return DateTime.UnixEpoch.AddSeconds(ToSeconds()).ToLocalTime().IsDaylightSavingTime();
} }
public static TimeSpanType FromSeconds(long seconds) public static TimeSpanType FromSeconds(long seconds)

View File

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

View File

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

View File

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

View File

@@ -2176,8 +2176,8 @@ namespace Ryujinx.Tests.Cpu
opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0);
opcodes |= ((q & 1) << 30); opcodes |= ((q & 1) << 30);
V128 v0 = MakeVectorE0E1(z, z); V128 v0 = MakeVectorE0E1(z, a);
V128 v1 = MakeVectorE0E1(a, a); V128 v1 = MakeVectorE0E1(a, z);
int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); int rnd = (int)TestContext.CurrentContext.Random.NextUInt();
@@ -2202,8 +2202,8 @@ namespace Ryujinx.Tests.Cpu
opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0);
opcodes |= ((q & 1) << 30); opcodes |= ((q & 1) << 30);
V128 v0 = MakeVectorE0E1(z, z); V128 v0 = MakeVectorE0E1(z, a);
V128 v1 = MakeVectorE0E1(a, a); V128 v1 = MakeVectorE0E1(a, z);
SingleOpcode(opcodes, v0: v0, v1: v1); SingleOpcode(opcodes, v0: v0, v1: v1);