Compare commits

..

8 Commits

Author SHA1 Message Date
92d166ecb7 Enable CPU JIT cache invalidation (#2965)
* Enable CPU JIT cache invalidation

* Invalidate cache on IC IVAU
2022-02-18 02:53:18 +01:00
72e543e946 Prefer texture over textureSize for sampler type (#3132)
* Prefer texture over textureSize for sampler type

* Shader cache version bump
2022-02-18 02:44:46 +01:00
98c838b24c Use BitOperations methods and delete now unused BitUtils methods (#3134)
Replaces BitUtils.CountTrailingZeros/CountLeadingZeros/IsPowerOfTwo with BitOperations methods
2022-02-18 02:35:23 +01:00
63c9c64196 Move kernel syscall logs to new trace log level (#3137) 2022-02-18 02:14:05 +01:00
a113ed0811 Implement/Stub mnpp:app service and some hid calls (#3131)
* Implement/Stub mnpp:app service and some hid calls

This PR Implement/Stub the `mnpp:app` service (closes #3107) accordingly to RE, which seems to do some telemetry for China region only, so everything is stubbed.

This PR fixes some inconsistencies in the hid service too and stub EnableSixAxisSensorUnalteredPassthrough, IsSixAxisSensorUnalteredPassthroughEnabled, LoadSixAxisSensorCalibrationParameter, GetSixAxisSensorIcInformation calls (closes #3123 and closes #3124).

* Addresses Thog review
2022-02-18 02:00:06 +01:00
747876dc67 Decoders: Add IOpCode32HasSetFlags (#3136) 2022-02-18 01:33:43 +01:00
95cc18a7b4 Added trace log level (#3096)
* added trace log level

* use trace log level instead of debug ( #1547)

* alignment #1547

* moved trace logs toggle at the bottom #1547

* bumped config file version #3096

* added migration step #3096

* setting moved to the dev section #1547

* performance warning displayed when trace is enabled #1547
2022-02-17 21:08:07 -03:00
c017c77365 Change ServiceNv map creation logs to the Debug level (#3058) 2022-02-18 00:41:21 +01:00
43 changed files with 1204 additions and 140 deletions

View File

@ -1,10 +1,8 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32Alu : IOpCode32
interface IOpCode32Alu : IOpCode32, IOpCode32HasSetFlags
{
int Rd { get; }
int Rn { get; }
bool? SetFlags { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32HasSetFlags
{
bool? SetFlags { get; }
}
}

View File

@ -1,6 +1,6 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluUmull : OpCode32
class OpCode32AluUmull : OpCode32, IOpCode32HasSetFlags
{
public int RdLo { get; }
public int RdHi { get; }

View File

@ -14,7 +14,7 @@ namespace ARMeilleure.Instructions
{
public static bool ShouldSetFlags(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
IOpCode32HasSetFlags op = (IOpCode32HasSetFlags)context.CurrOp;
if (op.SetFlags == null)
{

View File

@ -12,7 +12,8 @@ namespace ARMeilleure.Instructions
{
static partial class InstEmit
{
private const int DczSizeLog2 = 4;
private const int DczSizeLog2 = 4; // Log2 size in words
public const int DczSizeInBytes = 4 << DczSizeLog2;
public static void Hint(ArmEmitterContext context)
{
@ -87,7 +88,7 @@ namespace ARMeilleure.Instructions
// DC ZVA
Operand t = GetIntOrZR(context, op.Rt);
for (long offset = 0; offset < (4 << DczSizeLog2); offset += 8)
for (long offset = 0; offset < DczSizeInBytes; offset += 8)
{
Operand address = context.Add(t, Const(offset));
@ -98,7 +99,12 @@ namespace ARMeilleure.Instructions
}
// No-op
case 0b11_011_0111_1110_001: //DC CIVAC
case 0b11_011_0111_1110_001: // DC CIVAC
break;
case 0b11_011_0111_0101_001: // IC IVAU
Operand target = Register(op.Rt, RegisterType.Integer, OperandType.I64);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
break;
}
}

View File

@ -242,6 +242,11 @@ namespace ARMeilleure.Instructions
return (ulong)function.FuncPtr.ToInt64();
}
public static void InvalidateCacheLine(ulong address)
{
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
}
public static bool CheckSynchronization()
{
Statistics.PauseTimer();

View File

@ -114,6 +114,7 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));

View File

@ -0,0 +1,756 @@
using System;
using System.Collections.Generic;
namespace ARMeilleure.Translation
{
/// <summary>
/// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
/// </summary>
/// <typeparam name="K">Key</typeparam>
/// <typeparam name="V">Value</typeparam>
public class IntervalTree<K, V> where K : IComparable<K>
{
private const int ArrayGrowthSize = 32;
private const bool Black = true;
private const bool Red = false;
private IntervalTreeNode<K, V> _root = null;
private int _count = 0;
public int Count => _count;
public IntervalTree() { }
#region Public Methods
/// <summary>
/// Gets the values of the interval whose key is <paramref name="key"/>.
/// </summary>
/// <param name="key">Key of the node value to get</param>
/// <param name="value">Value with the given <paramref name="key"/></param>
/// <returns>True if the key is on the dictionary, false otherwise</returns>
public bool TryGet(K key, out V value)
{
IntervalTreeNode<K, V> node = GetNode(key);
if (node == null)
{
value = default;
return false;
}
value = node.Value;
return true;
}
/// <summary>
/// Returns the start addresses of the intervals whose start and end keys overlap the given range.
/// </summary>
/// <param name="start">Start of the range</param>
/// <param name="end">End of the range</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
/// <returns>Number of intervals found</returns>
public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0)
{
GetValues(_root, start, end, ref overlaps, ref overlapCount);
return overlapCount;
}
/// <summary>
/// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
/// </summary>
/// <param name="start">Start of the range to add</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to add</param>
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
}
/// <summary>
/// Gets an existing or adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
/// </summary>
/// <param name="start">Start of the range to add</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to add</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
public V GetOrAdd(K start, K end, V value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
return node.Value;
}
/// <summary>
/// Removes a value from the tree, searching for it with <paramref name="key"/>.
/// </summary>
/// <param name="key">Key of the node to remove</param>
/// <returns>Number of deleted values</returns>
public int Remove(K key)
{
int removed = Delete(key);
_count -= removed;
return removed;
}
/// <summary>
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
/// </summary>
/// <returns>A list of all values sorted by Key Order</returns>
public List<V> AsList()
{
List<V> list = new List<V>();
AddToList(_root, list);
return list;
}
#endregion
#region Private Methods (BST)
/// <summary>
/// Adds all values that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
/// </summary>
/// <param name="node">The node to search for values within</param>
/// <param name="list">The list to add values to</param>
private void AddToList(IntervalTreeNode<K, V> node, List<V> list)
{
if (node == null)
{
return;
}
AddToList(node.Left, list);
list.Add(node.Value);
AddToList(node.Right, list);
}
/// <summary>
/// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
/// </summary>
/// <param name="key">Key of the node to get</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
/// <returns>Node reference in the tree</returns>
private IntervalTreeNode<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
IntervalTreeNode<K, V> node = _root;
while (node != null)
{
int cmp = key.CompareTo(node.Start);
if (cmp < 0)
{
node = node.Left;
}
else if (cmp > 0)
{
node = node.Right;
}
else
{
return node;
}
}
return null;
}
/// <summary>
/// Retrieve all values that overlap the given start and end keys.
/// </summary>
/// <param name="start">Start of the range</param>
/// <param name="end">End of the range</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <param name="overlapCount">Overlaps count to update</param>
private void GetValues(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount)
{
if (node == null || start.CompareTo(node.Max) >= 0)
{
return;
}
GetValues(node.Left, start, end, ref overlaps, ref overlapCount);
bool endsOnRight = end.CompareTo(node.Start) > 0;
if (endsOnRight)
{
if (start.CompareTo(node.End) < 0)
{
if (overlaps.Length >= overlapCount)
{
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
}
overlaps[overlapCount++] = node.Start;
}
GetValues(node.Right, start, end, ref overlaps, ref overlapCount);
}
}
/// <summary>
/// Propagate an increase in max value starting at the given node, heading up the tree.
/// This should only be called if the max increases - not for rebalancing or removals.
/// </summary>
/// <param name="node">The node to start propagating from</param>
private void PropagateIncrease(IntervalTreeNode<K, V> node)
{
K max = node.Max;
IntervalTreeNode<K, V> ptr = node;
while ((ptr = ptr.Parent) != null)
{
if (max.CompareTo(ptr.Max) > 0)
{
ptr.Max = max;
}
else
{
break;
}
}
}
/// <summary>
/// Propagate recalculating max value starting at the given node, heading up the tree.
/// This fully recalculates the max value from all children when there is potential for it to decrease.
/// </summary>
/// <param name="node">The node to start propagating from</param>
private void PropagateFull(IntervalTreeNode<K, V> node)
{
IntervalTreeNode<K, V> ptr = node;
do
{
K max = ptr.End;
if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0)
{
max = ptr.Left.Max;
}
if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0)
{
max = ptr.Right.Max;
}
ptr.Max = max;
} while ((ptr = ptr.Parent) != null);
}
/// <summary>
/// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
/// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
/// Each node can contain multiple values, and has an end address which is the maximum of all those values.
/// Post insertion, the "max" value of the node and all parents are updated.
/// </summary>
/// <param name="start">Start of the range to insert</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to insert</param>
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
/// <param name="outNode">Node that was inserted or modified</param>
/// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns>
private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode)
{
IntervalTreeNode<K, V> parent = null;
IntervalTreeNode<K, V> node = _root;
while (node != null)
{
parent = node;
int cmp = start.CompareTo(node.Start);
if (cmp < 0)
{
node = node.Left;
}
else if (cmp > 0)
{
node = node.Right;
}
else
{
outNode = node;
if (updateFactoryCallback != null)
{
// Replace
node.Value = updateFactoryCallback(start, node.Value);
int endCmp = end.CompareTo(node.End);
if (endCmp > 0)
{
node.End = end;
if (end.CompareTo(node.Max) > 0)
{
node.Max = end;
PropagateIncrease(node);
RestoreBalanceAfterInsertion(node);
}
}
else if (endCmp < 0)
{
node.End = end;
PropagateFull(node);
}
}
return false;
}
}
IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent);
if (newNode.Parent == null)
{
_root = newNode;
}
else if (start.CompareTo(parent.Start) < 0)
{
parent.Left = newNode;
}
else
{
parent.Right = newNode;
}
PropagateIncrease(newNode);
_count++;
RestoreBalanceAfterInsertion(newNode);
outNode = newNode;
return true;
}
/// <summary>
/// Removes the value from the dictionary after searching for it with <paramref name="key">.
/// </summary>
/// <param name="key">Key to search for</param>
/// <returns>Number of deleted values</returns>
private int Delete(K key)
{
IntervalTreeNode<K, V> nodeToDelete = GetNode(key);
if (nodeToDelete == null)
{
return 0;
}
IntervalTreeNode<K, V> replacementNode;
if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
{
replacementNode = nodeToDelete;
}
else
{
replacementNode = PredecessorOf(nodeToDelete);
}
IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
if (tmp != null)
{
tmp.Parent = ParentOf(replacementNode);
}
if (ParentOf(replacementNode) == null)
{
_root = tmp;
}
else if (replacementNode == LeftOf(ParentOf(replacementNode)))
{
ParentOf(replacementNode).Left = tmp;
}
else
{
ParentOf(replacementNode).Right = tmp;
}
if (replacementNode != nodeToDelete)
{
nodeToDelete.Start = replacementNode.Start;
nodeToDelete.Value = replacementNode.Value;
nodeToDelete.End = replacementNode.End;
nodeToDelete.Max = replacementNode.Max;
}
PropagateFull(replacementNode);
if (tmp != null && ColorOf(replacementNode) == Black)
{
RestoreBalanceAfterRemoval(tmp);
}
return 1;
}
/// <summary>
/// Returns the node with the largest key where <paramref name="node"/> is considered the root node.
/// </summary>
/// <param name="node">Root Node</param>
/// <returns>Node with the maximum key in the tree of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> Maximum(IntervalTreeNode<K, V> node)
{
IntervalTreeNode<K, V> tmp = node;
while (tmp.Right != null)
{
tmp = tmp.Right;
}
return tmp;
}
/// <summary>
/// Finds the node whose key is immediately less than <paramref name="node"/>.
/// </summary>
/// <param name="node">Node to find the predecessor of</param>
/// <returns>Predecessor of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> PredecessorOf(IntervalTreeNode<K, V> node)
{
if (node.Left != null)
{
return Maximum(node.Left);
}
IntervalTreeNode<K, V> parent = node.Parent;
while (parent != null && node == parent.Left)
{
node = parent;
parent = parent.Parent;
}
return parent;
}
#endregion
#region Private Methods (RBL)
private void RestoreBalanceAfterRemoval(IntervalTreeNode<K, V> balanceNode)
{
IntervalTreeNode<K, V> ptr = balanceNode;
while (ptr != _root && ColorOf(ptr) == Black)
{
if (ptr == LeftOf(ParentOf(ptr)))
{
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ptr));
if (ColorOf(sibling) == Red)
{
SetColor(sibling, Black);
SetColor(ParentOf(ptr), Red);
RotateLeft(ParentOf(ptr));
sibling = RightOf(ParentOf(ptr));
}
if (ColorOf(LeftOf(sibling)) == Black && ColorOf(RightOf(sibling)) == Black)
{
SetColor(sibling, Red);
ptr = ParentOf(ptr);
}
else
{
if (ColorOf(RightOf(sibling)) == Black)
{
SetColor(LeftOf(sibling), Black);
SetColor(sibling, Red);
RotateRight(sibling);
sibling = RightOf(ParentOf(ptr));
}
SetColor(sibling, ColorOf(ParentOf(ptr)));
SetColor(ParentOf(ptr), Black);
SetColor(RightOf(sibling), Black);
RotateLeft(ParentOf(ptr));
ptr = _root;
}
}
else
{
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ptr));
if (ColorOf(sibling) == Red)
{
SetColor(sibling, Black);
SetColor(ParentOf(ptr), Red);
RotateRight(ParentOf(ptr));
sibling = LeftOf(ParentOf(ptr));
}
if (ColorOf(RightOf(sibling)) == Black && ColorOf(LeftOf(sibling)) == Black)
{
SetColor(sibling, Red);
ptr = ParentOf(ptr);
}
else
{
if (ColorOf(LeftOf(sibling)) == Black)
{
SetColor(RightOf(sibling), Black);
SetColor(sibling, Red);
RotateLeft(sibling);
sibling = LeftOf(ParentOf(ptr));
}
SetColor(sibling, ColorOf(ParentOf(ptr)));
SetColor(ParentOf(ptr), Black);
SetColor(LeftOf(sibling), Black);
RotateRight(ParentOf(ptr));
ptr = _root;
}
}
}
SetColor(ptr, Black);
}
private void RestoreBalanceAfterInsertion(IntervalTreeNode<K, V> balanceNode)
{
SetColor(balanceNode, Red);
while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red)
{
if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode))))
{
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ParentOf(balanceNode)));
if (ColorOf(sibling) == Red)
{
SetColor(ParentOf(balanceNode), Black);
SetColor(sibling, Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
balanceNode = ParentOf(ParentOf(balanceNode));
}
else
{
if (balanceNode == RightOf(ParentOf(balanceNode)))
{
balanceNode = ParentOf(balanceNode);
RotateLeft(balanceNode);
}
SetColor(ParentOf(balanceNode), Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
RotateRight(ParentOf(ParentOf(balanceNode)));
}
}
else
{
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ParentOf(balanceNode)));
if (ColorOf(sibling) == Red)
{
SetColor(ParentOf(balanceNode), Black);
SetColor(sibling, Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
balanceNode = ParentOf(ParentOf(balanceNode));
}
else
{
if (balanceNode == LeftOf(ParentOf(balanceNode)))
{
balanceNode = ParentOf(balanceNode);
RotateRight(balanceNode);
}
SetColor(ParentOf(balanceNode), Black);
SetColor(ParentOf(ParentOf(balanceNode)), Red);
RotateLeft(ParentOf(ParentOf(balanceNode)));
}
}
}
SetColor(_root, Black);
}
private void RotateLeft(IntervalTreeNode<K, V> node)
{
if (node != null)
{
IntervalTreeNode<K, V> right = RightOf(node);
node.Right = LeftOf(right);
if (node.Right != null)
{
node.Right.Parent = node;
}
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
right.Parent = nodeParent;
if (nodeParent == null)
{
_root = right;
}
else if (node == LeftOf(nodeParent))
{
nodeParent.Left = right;
}
else
{
nodeParent.Right = right;
}
right.Left = node;
node.Parent = right;
PropagateFull(node);
}
}
private void RotateRight(IntervalTreeNode<K, V> node)
{
if (node != null)
{
IntervalTreeNode<K, V> left = LeftOf(node);
node.Left = RightOf(left);
if (node.Left != null)
{
node.Left.Parent = node;
}
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
left.Parent = nodeParent;
if (nodeParent == null)
{
_root = left;
}
else if (node == RightOf(nodeParent))
{
nodeParent.Right = left;
}
else
{
nodeParent.Left = left;
}
left.Right = node;
node.Parent = left;
PropagateFull(node);
}
}
#endregion
#region Safety-Methods
// These methods save memory by allowing us to forego sentinel nil nodes, as well as serve as protection against NullReferenceExceptions.
/// <summary>
/// Returns the color of <paramref name="node"/>, or Black if it is null.
/// </summary>
/// <param name="node">Node</param>
/// <returns>The boolean color of <paramref name="node"/>, or black if null</returns>
private static bool ColorOf(IntervalTreeNode<K, V> node)
{
return node == null || node.Color;
}
/// <summary>
/// Sets the color of <paramref name="node"/> node to <paramref name="color"/>.
/// <br></br>
/// This method does nothing if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to set the color of</param>
/// <param name="color">Color (Boolean)</param>
private static void SetColor(IntervalTreeNode<K, V> node, bool color)
{
if (node != null)
{
node.Color = color;
}
}
/// <summary>
/// This method returns the left node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to retrieve the left child from</param>
/// <returns>Left child of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> LeftOf(IntervalTreeNode<K, V> node)
{
return node?.Left;
}
/// <summary>
/// This method returns the right node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to retrieve the right child from</param>
/// <returns>Right child of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> RightOf(IntervalTreeNode<K, V> node)
{
return node?.Right;
}
/// <summary>
/// Returns the parent node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
/// </summary>
/// <param name="node">Node to retrieve the parent from</param>
/// <returns>Parent of <paramref name="node"/></returns>
private static IntervalTreeNode<K, V> ParentOf(IntervalTreeNode<K, V> node)
{
return node?.Parent;
}
#endregion
public bool ContainsKey(K key)
{
return GetNode(key) != null;
}
public void Clear()
{
_root = null;
_count = 0;
}
}
/// <summary>
/// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
/// </summary>
/// <typeparam name="K">Key type of the node</typeparam>
/// <typeparam name="V">Value type of the node</typeparam>
internal class IntervalTreeNode<K, V>
{
internal bool Color = true;
internal IntervalTreeNode<K, V> Left = null;
internal IntervalTreeNode<K, V> Right = null;
internal IntervalTreeNode<K, V> Parent = null;
/// <summary>
/// The start of the range.
/// </summary>
internal K Start;
/// <summary>
/// The end of the range.
/// </summary>
internal K End;
/// <summary>
/// The maximum end value of this node and all its children.
/// </summary>
internal K Max;
/// <summary>
/// Value stored on this node.
/// </summary>
internal V Value;
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
{
this.Start = start;
this.End = end;
this.Max = end;
this.Value = value;
this.Parent = parent;
}
}
}

View File

@ -585,7 +585,7 @@ namespace ARMeilleure.Translation.PTC
translator.RegisterFunction(infoEntry.Address, func);
bool isAddressUnique = translator.Functions.TryAdd(infoEntry.Address, func);
bool isAddressUnique = translator.Functions.TryAdd(infoEntry.Address, infoEntry.GuestSize, func);
Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
}
@ -815,7 +815,7 @@ namespace ARMeilleure.Translation.PTC
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
bool isAddressUnique = translator.Functions.TryAdd(address, func);
bool isAddressUnique = translator.Functions.TryAdd(address, func.GuestSize, func);
Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");

View File

@ -96,7 +96,7 @@ namespace ARMeilleure.Translation.PTC
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
}
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> funcs)
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
{
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();

View File

@ -49,7 +49,7 @@ namespace ARMeilleure.Translation
private readonly AutoResetEvent _backgroundTranslatorEvent;
private readonly ReaderWriterLock _backgroundTranslatorLock;
internal ConcurrentDictionary<ulong, TranslatedFunction> Functions { get; }
internal TranslatorCache<TranslatedFunction> Functions { get; }
internal AddressTable<ulong> FunctionTable { get; }
internal EntryTable<uint> CountTable { get; }
internal TranslatorStubs Stubs { get; }
@ -75,7 +75,7 @@ namespace ARMeilleure.Translation
JitCache.Initialize(allocator);
CountTable = new EntryTable<uint>();
Functions = new ConcurrentDictionary<ulong, TranslatedFunction>();
Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? Levels64Bit : Levels32Bit);
Stubs = new TranslatorStubs(this);
@ -93,12 +93,12 @@ namespace ARMeilleure.Translation
{
_backgroundTranslatorLock.AcquireReaderLock(Timeout.Infinite);
if (_backgroundStack.TryPop(out RejitRequest request) &&
if (_backgroundStack.TryPop(out RejitRequest request) &&
_backgroundSet.TryRemove(request.Address, out _))
{
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
Functions.AddOrUpdate(request.Address, func, (key, oldFunc) =>
Functions.AddOrUpdate(request.Address, func.GuestSize, func, (key, oldFunc) =>
{
EnqueueForDeletion(key, oldFunc);
return func;
@ -196,7 +196,7 @@ namespace ARMeilleure.Translation
}
}
public ulong ExecuteSingle(State.ExecutionContext context, ulong address)
private ulong ExecuteSingle(State.ExecutionContext context, ulong address)
{
TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode);
@ -215,7 +215,7 @@ namespace ARMeilleure.Translation
{
func = Translate(address, mode, highCq: false);
TranslatedFunction oldFunc = Functions.GetOrAdd(address, func);
TranslatedFunction oldFunc = Functions.GetOrAdd(address, func.GuestSize, func);
if (oldFunc != func)
{
@ -471,7 +471,24 @@ namespace ARMeilleure.Translation
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
ClearRejitQueue(allowRequeue: true);
// TODO: Completely remove functions overlapping the specified range from the cache.
ulong[] overlapAddresses = Array.Empty<ulong>();
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
for (int index = 0; index < overlapsCount; index++)
{
ulong overlapAddress = overlapAddresses[index];
if (Functions.TryGetValue(overlapAddress, out TranslatedFunction overlap))
{
Functions.Remove(overlapAddress);
Volatile.Write(ref FunctionTable.GetValue(overlapAddress), FunctionTable.Fill);
EnqueueForDeletion(overlapAddress, overlap);
}
}
// TODO: Remove overlapping functions from the JitCache aswell.
// This should be done safely, with a mechanism to ensure the function is not being executed.
}
internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
@ -493,7 +510,9 @@ namespace ARMeilleure.Translation
// Ensure no attempt will be made to compile new functions due to rejit.
ClearRejitQueue(allowRequeue: false);
foreach (var func in Functions.Values)
List<TranslatedFunction> functions = Functions.AsList();
foreach (var func in functions)
{
JitCache.Unmap(func.FuncPtr);

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace ARMeilleure.Translation
{
internal class TranslatorCache<T>
{
private readonly IntervalTree<ulong, T> _tree;
private readonly ReaderWriterLock _treeLock;
public int Count => _tree.Count;
public TranslatorCache()
{
_tree = new IntervalTree<ulong, T>();
_treeLock = new ReaderWriterLock();
}
public bool TryAdd(ulong address, ulong size, T value)
{
return AddOrUpdate(address, size, value, null);
}
public bool AddOrUpdate(ulong address, ulong size, T value, Func<ulong, T, T> updateFactoryCallback)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
bool result = _tree.AddOrUpdate(address, address + size, value, updateFactoryCallback);
_treeLock.ReleaseWriterLock();
return result;
}
public T GetOrAdd(ulong address, ulong size, T value)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
value = _tree.GetOrAdd(address, address + size, value);
_treeLock.ReleaseWriterLock();
return value;
}
public bool Remove(ulong address)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
bool removed = _tree.Remove(address) != 0;
_treeLock.ReleaseWriterLock();
return removed;
}
public void Clear()
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_tree.Clear();
_treeLock.ReleaseWriterLock();
}
public bool ContainsKey(ulong address)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
bool result = _tree.ContainsKey(address);
_treeLock.ReleaseReaderLock();
return result;
}
public bool TryGetValue(ulong address, out T value)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
bool result = _tree.TryGet(address, out value);
_treeLock.ReleaseReaderLock();
return result;
}
public int GetOverlaps(ulong address, ulong size, ref ulong[] overlaps)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
int count = _tree.Get(address, address + size, ref overlaps);
_treeLock.ReleaseReaderLock();
return count;
}
public List<T> AsList()
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
List<T> list = _tree.AsList();
_treeLock.ReleaseReaderLock();
return list;
}
}
}

View File

@ -39,6 +39,7 @@ namespace Ryujinx.Common.Logging
ServiceLm,
ServiceMii,
ServiceMm,
ServiceMnpp,
ServiceNfc,
ServiceNfp,
ServiceNgct,

View File

@ -9,6 +9,7 @@ namespace Ryujinx.Common.Logging
Error,
Guest,
AccessLog,
Notice
Notice,
Trace
}
}

View File

@ -90,6 +90,7 @@ namespace Ryujinx.Common.Logging
public static Log? Guest { get; private set; }
public static Log? AccessLog { get; private set; }
public static Log? Stub { get; private set; }
public static Log? Trace { get; private set; }
public static Log Notice { get; } // Always enabled
static Logger()
@ -117,6 +118,7 @@ namespace Ryujinx.Common.Logging
Error = new Log(LogLevel.Error);
Warning = new Log(LogLevel.Warning);
Info = new Log(LogLevel.Info);
Trace = new Log(LogLevel.Trace);
}
public static void RestartTime()
@ -172,7 +174,7 @@ namespace Ryujinx.Common.Logging
public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
{
var logs = new Log?[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub };
var logs = new Log?[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
List<LogLevel> levels = new List<LogLevel>(logs.Length);
foreach (var log in logs)
{
@ -196,6 +198,7 @@ namespace Ryujinx.Common.Logging
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : new Log?(); break;
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog): new Log?(); break;
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : new Log?(); break;
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : new Log?(); break;
default: throw new ArgumentException("Unknown Log Level");
}
}

View File

@ -17,6 +17,7 @@ namespace Ryujinx.Common.Logging
LogLevel.Error => ConsoleColor.Red,
LogLevel.Stub => ConsoleColor.DarkGray,
LogLevel.Notice => ConsoleColor.Cyan,
LogLevel.Trace => ConsoleColor.DarkCyan,
_ => ConsoleColor.Gray,
};

View File

@ -1,11 +1,10 @@
using System;
using System.Numerics;
namespace Ryujinx.Common
{
public static class BitUtils
{
private static ReadOnlySpan<byte> ClzNibbleTbl => new byte[] { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
public static uint AlignUp(uint value, int size)
{
return (uint)AlignUp((int)value, size);
@ -76,60 +75,7 @@ namespace Ryujinx.Common
public static int Pow2RoundDown(int value)
{
return IsPowerOfTwo32(value) ? value : Pow2RoundUp(value) >> 1;
}
public static bool IsPowerOfTwo32(int value)
{
return value != 0 && (value & (value - 1)) == 0;
}
public static bool IsPowerOfTwo64(long value)
{
return value != 0 && (value & (value - 1)) == 0;
}
public static int CountLeadingZeros32(int value)
{
return (int)CountLeadingZeros((ulong)value, 32);
}
public static int CountLeadingZeros64(long value)
{
return (int)CountLeadingZeros((ulong)value, 64);
}
private static ulong CountLeadingZeros(ulong value, int size)
{
if (value == 0ul)
{
return (ulong)size;
}
int nibbleIdx = size;
int preCount, count = 0;
do
{
nibbleIdx -= 4;
preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
count += preCount;
}
while (preCount == 4);
return (ulong)count;
}
public static int CountTrailingZeros32(int value)
{
int count = 0;
while (((value >> count) & 1) == 0)
{
count++;
}
return count;
return BitOperations.IsPow2(value) ? value : Pow2RoundUp(value) >> 1;
}
public static long ReverseBits64(long value)

View File

@ -28,5 +28,10 @@ namespace Ryujinx.Cpu
{
_translator.Execute(context, address);
}
public void InvalidateCacheRegion(ulong address, ulong size)
{
_translator.InvalidateJitCacheRegion(address, size);
}
}
}

View File

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Version of the codegen (to be changed when codegen or guest format change).
/// </summary>
private const ulong ShaderCodeGenVersion = 3063;
private const ulong ShaderCodeGenVersion = 3132;
// Progress reporting helpers
private volatile int _shaderCount;

View File

@ -369,7 +369,7 @@ namespace Ryujinx.Graphics.Shader.Translation
inst &= Instruction.Mask;
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
bool accurateType = inst != Instruction.Lod;
bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize;
bool coherent = flags.HasFlag(TextureFlags.Coherent);
if (isImage)

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using System.Numerics;
using System.Runtime.CompilerServices;
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
@ -47,15 +48,15 @@ namespace Ryujinx.Graphics.Texture
{
_texBpp = bpp;
_bppShift = BitUtils.CountTrailingZeros32(bpp);
_bppShift = BitOperations.TrailingZeroCount(bpp);
_bhMask = gobBlocksInY - 1;
_bdMask = gobBlocksInZ - 1;
_bhShift = BitUtils.CountTrailingZeros32(gobBlocksInY);
_bdShift = BitUtils.CountTrailingZeros32(gobBlocksInZ);
_bhShift = BitOperations.TrailingZeroCount(gobBlocksInY);
_bdShift = BitOperations.TrailingZeroCount(gobBlocksInZ);
_xShift = BitUtils.CountTrailingZeros32(GobSize * gobBlocksInY * gobBlocksInZ);
_xShift = BitOperations.TrailingZeroCount(GobSize * gobBlocksInY * gobBlocksInZ);
RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gobBlocksInY, gobBlocksInZ);

View File

@ -36,6 +36,11 @@ namespace Ryujinx.HLE.HOS
_cpuContext.Execute(context, codeAddress);
}
public void InvalidateCacheRegion(ulong address, ulong size)
{
_cpuContext.InvalidateCacheRegion(address, size);
}
public void Dispose()
{
if (_memoryManager is IRefCounted rc)

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Common
{
@ -41,10 +42,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
range++;
// This is log2(Range) plus one.
int nextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(range);
int nextRangeLog2 = 64 - BitOperations.LeadingZeroCount((ulong)range);
// If Range is already power of 2, subtract one to use log2(Range) directly.
int rangeLog2 = nextRangeLog2 - (BitUtils.IsPowerOfTwo64(range) ? 1 : 0);
int rangeLog2 = nextRangeLog2 - (BitOperations.IsPow2(range) ? 1 : 0);
int parts = rangeLog2 > 32 ? 2 : 1;
int bitsPerPart = rangeLog2 / parts;

View File

@ -1,6 +1,7 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
using System.Diagnostics;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
@ -259,11 +260,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (backwards)
{
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
index = (index * 64 + 63) - BitOperations.LeadingZeroCount((ulong)mask);
}
else
{
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
index = index * 64 + BitOperations.LeadingZeroCount((ulong)BitUtils.ReverseBits64(mask));
}
}
@ -312,11 +313,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (backwards)
{
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
index = index * 64 + BitOperations.LeadingZeroCount((ulong)BitUtils.ReverseBits64(mask));
}
else
{
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
index = (index * 64 + 63) - BitOperations.LeadingZeroCount((ulong)mask);
}
}

View File

@ -9,5 +9,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
IVirtualMemoryManager AddressSpace { get; }
void Execute(ExecutionContext context, ulong codeAddress);
void InvalidateCacheRegion(ulong address, ulong size);
}
}

View File

@ -1,5 +1,6 @@
using Ryujinx.Common;
using System;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
@ -32,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
int mask = _idMasks[index];
int firstFreeBit = BitUtils.CountLeadingZeros32((mask + 1) & ~mask);
int firstFreeBit = BitOperations.LeadingZeroCount((uint)((mask + 1) & ~mask));
if (firstFreeBit < 32)
{

View File

@ -3,6 +3,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
@ -130,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.Success;
}
int codeMask = 1 << (32 - BitUtils.CountLeadingZeros32(code + 1));
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1));
// Check if the property was already set.
if (((mask0 & codeMask) & 0x1e008) != 0)

View File

@ -18,6 +18,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
throw new NotSupportedException();
}
public void InvalidateCacheRegion(ulong address, ulong size)
{
}
public void Dispose()
{
}

View File

@ -459,11 +459,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
if (argValues != null)
{
Logger.Debug?.Print(LogClass.KernelSvc, string.Format(formatOrSvcName, argValues));
Logger.Trace?.Print(LogClass.KernelSvc, string.Format(formatOrSvcName, argValues));
}
else
{
Logger.Debug?.Print(LogClass.KernelSvc, formatOrSvcName);
Logger.Trace?.Print(LogClass.KernelSvc, formatOrSvcName);
}
}
@ -479,7 +479,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
else
{
Logger.Debug?.Print(LogClass.KernelSvc, $"{svcName} returned result {result}.");
Logger.Trace?.Print(LogClass.KernelSvc, $"{svcName} returned result {result}.");
}
}
}

View File

@ -15,12 +15,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetAppletResourceUserId() -> nn::applet::AppletResourceUserId
public ResultCode GetAppletResourceUserId(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
long appletResourceUserId = context.Device.System.AppletState.AppletResourceUserIds.Add(_pid);
context.ResponseData.Write(appletResourceUserId);
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { appletResourceUserId });
return ResultCode.Success;
}

View File

@ -24,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private bool _vibrationPermitted;
private bool _usbFullKeyControllerEnabled;
private bool _isFirmwareUpdateAvailableForSixAxisSensor;
private bool _isSixAxisSensorUnalteredPassthroughEnabled;
private HidNpadJoyAssignmentMode _npadJoyAssignmentMode;
private HidNpadHandheldActivationMode _npadHandheldActivationMode;
@ -335,7 +336,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// StartSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode StartSixAxisSensor(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
@ -347,7 +349,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// StopSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode StopSixAxisSensor(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
@ -359,7 +362,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// IsSixAxisSensorFusionEnabled(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsEnabled
public ResultCode IsSixAxisSensorFusionEnabled(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write(_sixAxisSensorFusionEnabled);
@ -373,9 +377,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// EnableSixAxisSensorFusion(bool Enabled, nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode EnableSixAxisSensorFusion(ServiceCtx context)
{
_sixAxisSensorFusionEnabled = context.RequestData.ReadBoolean();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
_sixAxisSensorFusionEnabled = context.RequestData.ReadUInt32() != 0;
int sixAxisSensorHandle = context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _sixAxisSensorFusionEnabled });
@ -386,7 +390,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// SetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, float RevisePower, float ReviseRange, nn::applet::AppletResourceUserId)
public ResultCode SetSixAxisSensorFusionParameters(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
_sensorFusionParams = new HidSensorFusionParameters
{
@ -405,7 +410,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// GetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float RevisePower, float ReviseRange)
public ResultCode GetSixAxisSensorFusionParameters(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write(_sensorFusionParams.RevisePower);
@ -420,7 +426,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// ResetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode ResetSixAxisSensorFusionParameters(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_sensorFusionParams.RevisePower = 0;
@ -436,6 +443,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public ResultCode SetAccelerometerParameters(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
_accelerometerParams = new HidAccelerometerParameters
{
@ -454,7 +462,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// GetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float X, float Y
public ResultCode GetAccelerometerParameters(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write(_accelerometerParams.X);
@ -469,7 +478,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// ResetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode ResetAccelerometerParameters(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_accelerometerParams.X = 0;
@ -484,9 +494,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// SetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, uint PlayMode, nn::applet::AppletResourceUserId)
public ResultCode SetAccelerometerPlayMode(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
_accelerometerPlayMode = context.RequestData.ReadUInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
_accelerometerPlayMode = context.RequestData.ReadUInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _accelerometerPlayMode });
@ -497,7 +508,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// GetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> uint PlayMode
public ResultCode GetAccelerometerPlayMode(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write(_accelerometerPlayMode);
@ -511,7 +523,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// ResetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode ResetAccelerometerPlayMode(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_accelerometerPlayMode = 0;
@ -525,9 +538,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// SetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, uint GyroscopeZeroDriftMode, nn::applet::AppletResourceUserId)
public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
_gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
_gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
@ -538,7 +551,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// GetGyroscopeZeroDriftMode(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> int GyroscopeZeroDriftMode
public ResultCode GetGyroscopeZeroDriftMode(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write((int)_gyroscopeZeroDriftMode);
@ -552,7 +566,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// ResetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
public ResultCode ResetGyroscopeZeroDriftMode(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
@ -566,7 +581,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest
public ResultCode IsSixAxisSensorAtRest(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
bool isAtRest = true;
@ -582,8 +598,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// IsFirmwareUpdateAvailableForSixAxisSensor(nn::hid::AppletResourceUserId, nn::hid::SixAxisSensorHandle, pid) -> bool UpdateAvailable
public ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4;
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write(_isFirmwareUpdateAvailableForSixAxisSensor);
@ -593,6 +609,64 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return ResultCode.Success;
}
[CommandHipc(84)] // 13.0.0+
// EnableSixAxisSensorUnalteredPassthrough(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle, u8 enabled)
public ResultCode EnableSixAxisSensorUnalteredPassthrough(ServiceCtx context)
{
_isSixAxisSensorUnalteredPassthroughEnabled = context.RequestData.ReadUInt32() != 0;
int sixAxisSensorHandle = context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _isSixAxisSensorUnalteredPassthroughEnabled });
return ResultCode.Success;
}
[CommandHipc(85)] // 13.0.0+
// IsSixAxisSensorUnalteredPassthroughEnabled(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> u8 enabled
public ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write(_isSixAxisSensorUnalteredPassthroughEnabled);
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
return ResultCode.Success;
}
[CommandHipc(87)] // 13.0.0+
// LoadSixAxisSensorCalibrationParameter(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle, u64 unknown)
public ResultCode LoadSixAxisSensorCalibrationParameter(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
// TODO: CalibrationParameter have to be determined.
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
return ResultCode.Success;
}
[CommandHipc(88)] // 13.0.0+
// GetSixAxisSensorIcInformation(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> u64 unknown
public ResultCode GetSixAxisSensorIcInformation(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
// TODO: IcInformation have to be determined.
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
return ResultCode.Success;
}
[CommandHipc(91)]
// ActivateGesture(nn::applet::AppletResourceUserId, int Unknown0)
public ResultCode ActivateGesture(ServiceCtx context)
@ -606,16 +680,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid
}
[CommandHipc(100)]
// SetSupportedNpadStyleSet(nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag)
// SetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag)
public ResultCode SetSupportedNpadStyleSet(ServiceCtx context)
{
ulong pid = context.Request.HandleDesc.PId;
ControllerType type = (ControllerType)context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
appletResourceUserId,
type
});
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { pid, appletResourceUserId, type });
context.Device.Hid.Npads.SupportedStyleSets = type;
@ -623,17 +696,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid
}
[CommandHipc(101)]
// GetSupportedNpadStyleSet(nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag
// GetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag
public ResultCode GetSupportedNpadStyleSet(ServiceCtx context)
{
long appletResourceUserId = context.RequestData.ReadInt64();
ulong pid = context.Request.HandleDesc.PId;
long appletResourceUserId = context.RequestData.ReadInt64();
context.ResponseData.Write((int)context.Device.Hid.Npads.SupportedStyleSets);
Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
appletResourceUserId,
context.Device.Hid.Npads.SupportedStyleSets
});
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, context.Device.Hid.Npads.SupportedStyleSets });
return ResultCode.Success;
}
@ -658,7 +729,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
}
}
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{supportedPlayerIds.Length} " + string.Join(",", supportedPlayerIds.ToArray()));
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{supportedPlayerIds.Length} Players: " + string.Join(",", supportedPlayerIds.ToArray()));
return ResultCode.Success;
}

View File

@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services
if (serviceExists)
{
Logger.Debug?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
Logger.Trace?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
result = (ResultCode)processRequest.Invoke(service, new object[] { context });
}

View File

@ -0,0 +1,63 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Account.Acc;
namespace Ryujinx.HLE.HOS.Services.Mnpp
{
[Service("mnpp:app")] // 13.0.0+
class IServiceForApplication : IpcService
{
public IServiceForApplication(ServiceCtx context) { }
[CommandHipc(0)]
// Initialize(pid)
public ResultCode Initialize(ServiceCtx context)
{
// Pid placeholder
context.RequestData.ReadInt64();
ulong pid = context.Request.HandleDesc.PId;
// TODO: Service calls set:sys GetPlatformRegion.
// If the result == 1 (China) it calls arp:r GetApplicationInstanceId and GetApplicationLaunchProperty to get the title id and store it internally.
// If not, it does nothing.
Logger.Stub?.PrintStub(LogClass.ServiceMnpp, new { pid });
return ResultCode.Success;
}
[CommandHipc(1)]
// SendRawTelemetryData(nn::account::Uid user_id, buffer<bytes, 5> title_id)
public ResultCode SendRawTelemetryData(ServiceCtx context)
{
ulong titleIdInputPosition = context.Request.SendBuff[0].Position;
ulong titleIdInputSize = context.Request.SendBuff[0].Size;
UserId userId = context.RequestData.ReadStruct<UserId>();
// TODO: Service calls set:sys GetPlatformRegion.
// If the result != 1 (China) it returns ResultCode.Success.
if (userId.IsNull)
{
return ResultCode.InvalidArgument;
}
if (titleIdInputSize <= 64)
{
string titleId = MemoryHelper.ReadAsciiString(context.Memory, titleIdInputPosition, (long)titleIdInputSize);
// TODO: The service stores the titleId internally and seems proceed to some telemetry for China, which is not needed here.
Logger.Stub?.PrintStub(LogClass.ServiceMnpp, new { userId, titleId });
return ResultCode.Success;
}
Logger.Stub?.PrintStub(LogClass.ServiceMnpp, new { userId });
return ResultCode.InvalidBufferSize;
}
}
}

View File

@ -0,0 +1,13 @@
namespace Ryujinx.HLE.HOS.Services.Mnpp
{
enum ResultCode
{
ModuleId = 239,
ErrorCodeShift = 9,
Success = 0,
InvalidArgument = (100 << ErrorCodeShift) | ModuleId,
InvalidBufferSize = (101 << ErrorCodeShift) | ModuleId
}
}

View File

@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
arguments.Handle = CreateHandleFromMap(new NvMapHandle(size));
Logger.Info?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!");
Logger.Debug?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!");
return NvInternalResult.Success;
}
@ -265,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
DeleteMapWithHandle(pid, handle);
Logger.Info?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
return true;
}

View File

@ -135,6 +135,9 @@ namespace Ryujinx.Headless.SDL2
[Option("enable-error-logs", Required = false, Default = true, HelpText = "Enables printing error log messages.")]
public bool? LoggingEnableError { get; set; }
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
public bool? LoggingEnableTrace { get; set; }
[Option("enable-guest-logs", Required = false, Default = true, HelpText = "Enables printing guest log messages.")]
public bool? LoggingEnableGuest { get; set; }

View File

@ -389,6 +389,7 @@ namespace Ryujinx.Headless.SDL2
Logger.SetEnable(LogLevel.Info, (bool)option.LoggingEnableInfo);
Logger.SetEnable(LogLevel.Warning, (bool)option.LoggingEnableWarning);
Logger.SetEnable(LogLevel.Error, (bool)option.LoggingEnableError);
Logger.SetEnable(LogLevel.Trace, (bool)option.LoggingEnableTrace);
Logger.SetEnable(LogLevel.Guest, (bool)option.LoggingEnableGuest);
Logger.SetEnable(LogLevel.AccessLog, (bool)option.LoggingEnableFsAccessLog);

View File

@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 35;
public const int CurrentVersion = 36;
/// <summary>
/// Version of the configuration file format
@ -81,6 +81,11 @@ namespace Ryujinx.Configuration
/// </summary>
public bool LoggingEnableError { get; set; }
/// <summary>
/// Enables printing trace log messages
/// </summary>
public bool LoggingEnableTrace { get; set; }
/// <summary>
/// Enables printing guest log messages
/// </summary>

View File

@ -129,6 +129,11 @@ namespace Ryujinx.Configuration
/// </summary>
public ReactiveObject<bool> EnableError { get; private set; }
/// <summary>
/// Enables printing trace log messages
/// </summary>
public ReactiveObject<bool> EnableTrace { get; private set; }
/// <summary>
/// Enables printing guest log messages
/// </summary>
@ -161,6 +166,7 @@ namespace Ryujinx.Configuration
EnableInfo = new ReactiveObject<bool>();
EnableWarn = new ReactiveObject<bool>();
EnableError = new ReactiveObject<bool>();
EnableTrace = new ReactiveObject<bool>();
EnableGuest = new ReactiveObject<bool>();
EnableFsAccessLog = new ReactiveObject<bool>();
FilteredClasses = new ReactiveObject<LogClass[]>();
@ -455,6 +461,7 @@ namespace Ryujinx.Configuration
LoggingEnableInfo = Logger.EnableInfo,
LoggingEnableWarn = Logger.EnableWarn,
LoggingEnableError = Logger.EnableError,
LoggingEnableTrace = Logger.EnableTrace,
LoggingEnableGuest = Logger.EnableGuest,
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
LoggingFilteredClasses = Logger.FilteredClasses,
@ -526,6 +533,7 @@ namespace Ryujinx.Configuration
Logger.EnableInfo.Value = true;
Logger.EnableWarn.Value = true;
Logger.EnableError.Value = true;
Logger.EnableTrace.Value = false;
Logger.EnableGuest.Value = true;
Logger.EnableFsAccessLog.Value = false;
Logger.FilteredClasses.Value = Array.Empty<LogClass>();
@ -990,6 +998,15 @@ namespace Ryujinx.Configuration
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 36)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 36.");
configurationFileFormat.LoggingEnableTrace = false;
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
@ -1003,6 +1020,7 @@ namespace Ryujinx.Configuration
Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn;
Logger.EnableError.Value = configurationFileFormat.LoggingEnableError;
Logger.EnableTrace.Value = configurationFileFormat.LoggingEnableTrace;
Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest;
Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog;
Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses;

View File

@ -13,6 +13,7 @@ namespace Ryujinx.Configuration
ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo;
ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning;
ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError;
ConfigurationState.Instance.Logger.EnableTrace.Event += ReloadEnableTrace;
ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest;
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog;
ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses;
@ -44,6 +45,11 @@ namespace Ryujinx.Configuration
Logger.SetEnable(LogLevel.Error, e.NewValue);
}
private static void ReloadEnableTrace(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Trace, e.NewValue);
}
private static void ReloadEnableGuest(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Guest, e.NewValue);

View File

@ -638,18 +638,18 @@ namespace Ryujinx.Ui
[Conditional("RELEASE")]
public void PerformanceCheck()
{
if (ConfigurationState.Instance.Logger.EnableDebug.Value)
if (ConfigurationState.Instance.Logger.EnableTrace.Value)
{
MessageDialog debugWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
{
Title = "Ryujinx - Warning",
Text = "You have debug logging enabled, which is designed to be used by developers only.",
SecondaryText = "For optimal performance, it's recommended to disable debug logging. Would you like to disable debug logging now?"
Text = "You have trace logging enabled, which is designed to be used by developers only.",
SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?"
};
if (debugWarningDialog.Run() == (int)ResponseType.Yes)
{
ConfigurationState.Instance.Logger.EnableDebug.Value = false;
ConfigurationState.Instance.Logger.EnableTrace.Value = false;
SaveConfig();
}

View File

@ -35,6 +35,7 @@ namespace Ryujinx.Ui.Windows
private float _previousVolumeLevel;
#pragma warning disable CS0649, IDE0044
[GUI] CheckButton _traceLogToggle;
[GUI] CheckButton _errorLogToggle;
[GUI] CheckButton _warningLogToggle;
[GUI] CheckButton _infoLogToggle;
@ -141,6 +142,11 @@ namespace Ryujinx.Ui.Windows
};
// Setup Currents.
if (ConfigurationState.Instance.Logger.EnableTrace)
{
_traceLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableFileLog)
{
_fileLogToggle.Click();
@ -487,6 +493,7 @@ namespace Ryujinx.Ui.Windows
}
ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active;
ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;

View File

@ -2572,6 +2572,24 @@
<property name="position">21</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="_traceLogToggle">
<property name="label" translatable="yes">Enable Trace Logs</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="tooltip-text" translatable="yes">Enables printing trace log messages</property>
<property name="halign">start</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">22</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>