mirror of
https://git.zaroz.cloud/nintendo-back-up/Ryujinx.git
synced 2025-12-31 06:33:00 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f293f8cbe | ||
|
|
32a1cd83fd | ||
|
|
e3d0ccf8d5 | ||
|
|
c14844d12c |
@@ -16,4 +16,10 @@
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Ryujinx.Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
@@ -32,9 +31,12 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR)
|
||||
{
|
||||
ulong value = operand.Value;
|
||||
return TryEncodeBitMask(operand.Type, operand.Value, out immN, out immS, out immR);
|
||||
}
|
||||
|
||||
if (operand.Type == OperandType.I32)
|
||||
public static bool TryEncodeBitMask(OperandType type, ulong value, out int immN, out int immS, out int immR)
|
||||
{
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
value |= value << 32;
|
||||
}
|
||||
@@ -50,7 +52,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
// Any value AND all ones will be equal itself, so it's effectively a no-op.
|
||||
// Any value OR all ones will be equal all ones, so one can just use MOV.
|
||||
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
|
||||
if (value == ulong.MaxValue)
|
||||
if (value == 0 || value == ulong.MaxValue)
|
||||
{
|
||||
immN = 0;
|
||||
immS = 0;
|
||||
@@ -59,79 +61,18 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
return false;
|
||||
}
|
||||
|
||||
int bitLength = CountSequence(value);
|
||||
// Normalize value, rotating it such that the LSB is 1: Ensures we get a complete element that has not
|
||||
// been cut-in-half across the word boundary.
|
||||
int rotation = BitOperations.TrailingZeroCount(value & (value + 1));
|
||||
ulong rotatedValue = ulong.RotateRight(value, rotation);
|
||||
|
||||
if ((value >> bitLength) != 0)
|
||||
{
|
||||
bitLength += CountSequence(value >> bitLength);
|
||||
}
|
||||
// Now that we have a complete element in the LSB with the LSB = 1, determine size and number of ones
|
||||
// in element.
|
||||
int elementSize = BitOperations.TrailingZeroCount(rotatedValue & (rotatedValue + 1));
|
||||
int onesInElement = BitOperations.TrailingZeroCount(~rotatedValue);
|
||||
|
||||
int bitLengthLog2 = BitOperations.Log2((uint)bitLength);
|
||||
int bitLengthPow2 = 1 << bitLengthLog2;
|
||||
|
||||
if (bitLengthPow2 < bitLength)
|
||||
{
|
||||
bitLengthLog2++;
|
||||
bitLengthPow2 <<= 1;
|
||||
}
|
||||
|
||||
int selectedESize = 64;
|
||||
int repetitions = 1;
|
||||
int onesCount = BitOperations.PopCount(value);
|
||||
|
||||
if (bitLengthPow2 < 64 && (value >> bitLengthPow2) != 0)
|
||||
{
|
||||
for (int eSizeLog2 = bitLengthLog2; eSizeLog2 < 6; eSizeLog2++)
|
||||
{
|
||||
bool match = true;
|
||||
int eSize = 1 << eSizeLog2;
|
||||
ulong mask = (1UL << eSize) - 1;
|
||||
ulong eValue = value & mask;
|
||||
|
||||
for (int e = 1; e < 64 / eSize; e++)
|
||||
{
|
||||
if (((value >> (e * eSize)) & mask) != eValue)
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
selectedESize = eSize;
|
||||
repetitions = 64 / eSize;
|
||||
onesCount = BitOperations.PopCount(eValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find rotation. We have two cases, one where the highest bit is 0
|
||||
// and one where it is 1.
|
||||
// If it's 1, we just need to count the number of 1 bits on the MSB to find the right rotation.
|
||||
// If it's 0, we just need to count the number of 0 bits on the LSB to find the left rotation,
|
||||
// then we can convert it to the right rotation shift by subtracting the value from the element size.
|
||||
int rotation;
|
||||
long vHigh = (long)(value << (64 - selectedESize));
|
||||
if (vHigh < 0)
|
||||
{
|
||||
rotation = BitOperations.LeadingZeroCount(~(ulong)vHigh);
|
||||
}
|
||||
else
|
||||
{
|
||||
rotation = (selectedESize - BitOperations.TrailingZeroCount(value)) & (selectedESize - 1);
|
||||
}
|
||||
|
||||
// Reconstruct value and see if it matches. If not, we can't encode.
|
||||
ulong reconstructed = onesCount == 64 ? ulong.MaxValue : RotateRight((1UL << onesCount) - 1, rotation, selectedESize);
|
||||
|
||||
for (int bit = 32; bit >= selectedESize; bit >>= 1)
|
||||
{
|
||||
reconstructed |= reconstructed << bit;
|
||||
}
|
||||
|
||||
if (reconstructed != value || onesCount == 0)
|
||||
// Check the value is repeating; also ensures element size is a power of two.
|
||||
if (ulong.RotateRight(value, elementSize) != value)
|
||||
{
|
||||
immN = 0;
|
||||
immS = 0;
|
||||
@@ -140,34 +81,11 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
return false;
|
||||
}
|
||||
|
||||
immR = rotation;
|
||||
|
||||
// immN indicates that there are no repetitions.
|
||||
// The MSB of immS indicates the amount of repetitions, and the LSB the number of bits set.
|
||||
if (repetitions == 1)
|
||||
{
|
||||
immN = 1;
|
||||
immS = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
immN = 0;
|
||||
immS = (0xf80 >> BitOperations.Log2((uint)repetitions)) & 0x3f;
|
||||
}
|
||||
|
||||
immS |= onesCount - 1;
|
||||
immN = (elementSize >> 6) & 1;
|
||||
immS = (((~elementSize + 1) << 1) | (onesInElement - 1)) & 0x3f;
|
||||
immR = (elementSize - rotation) & (elementSize - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int CountSequence(ulong value)
|
||||
{
|
||||
return BitOperations.TrailingZeroCount(value) + BitOperations.TrailingZeroCount(~value);
|
||||
}
|
||||
|
||||
private static ulong RotateRight(ulong bits, int shift, int size)
|
||||
{
|
||||
return (bits >> shift) | ((bits << (size - shift)) & (size == 64 ? ulong.MaxValue : (1UL << size) - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1303,7 +1303,15 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value)
|
||||
{
|
||||
if (value != 0)
|
||||
if (value == 0)
|
||||
{
|
||||
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
|
||||
}
|
||||
else if (CodeGenCommon.TryEncodeBitMask(dest.Type, value, out _, out _, out _))
|
||||
{
|
||||
context.Assembler.Orr(dest, Register(ZrRegister, dest.Type), Const(dest.Type, (long)value));
|
||||
}
|
||||
else
|
||||
{
|
||||
int hw = 0;
|
||||
bool first = true;
|
||||
@@ -1328,10 +1336,6 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
value >>= 16;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateAtomicCas(
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 4272; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 4328; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e)
|
||||
private void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e)
|
||||
{
|
||||
if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0)
|
||||
{
|
||||
@@ -240,7 +240,7 @@ namespace Ryujinx.Ava
|
||||
lock (_lockObject)
|
||||
{
|
||||
DateTime currentTime = DateTime.Now;
|
||||
string filename = $"ryujinx_capture_{currentTime}-{currentTime:D2}-{currentTime:D2}_{currentTime:D2}-{currentTime:D2}-{currentTime:D2}.png";
|
||||
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
|
||||
|
||||
string directory = AppDataManager.Mode switch
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
@@ -30,6 +31,7 @@ namespace Ryujinx.Ava.Input
|
||||
_control.KeyDown += OnKeyPress;
|
||||
_control.KeyUp += OnKeyRelease;
|
||||
_control.TextInput += Control_TextInput;
|
||||
_control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble);
|
||||
}
|
||||
|
||||
private void Control_TextInput(object sender, TextInputEventArgs e)
|
||||
@@ -37,6 +39,12 @@ namespace Ryujinx.Ava.Input
|
||||
TextInput?.Invoke(this, e.Text);
|
||||
}
|
||||
|
||||
private void Control_LastChanceTextInput(object sender, TextInputEventArgs e)
|
||||
{
|
||||
// Swallow event
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
public event Action<string> OnGamepadConnected
|
||||
{
|
||||
add { }
|
||||
|
||||
@@ -140,68 +140,75 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
{
|
||||
if (VisualRoot != null)
|
||||
{
|
||||
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
|
||||
Pointer pointer = new(0, PointerType.Mouse, true);
|
||||
|
||||
switch (msg)
|
||||
if (msg == WindowsMessages.LBUTTONDOWN ||
|
||||
msg == WindowsMessages.RBUTTONDOWN ||
|
||||
msg == WindowsMessages.LBUTTONUP ||
|
||||
msg == WindowsMessages.RBUTTONUP ||
|
||||
msg == WindowsMessages.MOUSEMOVE)
|
||||
{
|
||||
case WindowsMessages.LBUTTONDOWN:
|
||||
case WindowsMessages.RBUTTONDOWN:
|
||||
{
|
||||
bool isLeft = msg == WindowsMessages.LBUTTONDOWN;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
|
||||
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
|
||||
Pointer pointer = new(0, PointerType.Mouse, true);
|
||||
|
||||
var evnt = new PointerPressedEventArgs(
|
||||
this,
|
||||
pointer,
|
||||
VisualRoot,
|
||||
rootVisualPosition,
|
||||
(ulong)Environment.TickCount64,
|
||||
properties,
|
||||
KeyModifiers.None);
|
||||
switch (msg)
|
||||
{
|
||||
case WindowsMessages.LBUTTONDOWN:
|
||||
case WindowsMessages.RBUTTONDOWN:
|
||||
{
|
||||
bool isLeft = msg == WindowsMessages.LBUTTONDOWN;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
|
||||
|
||||
RaiseEvent(evnt);
|
||||
var evnt = new PointerPressedEventArgs(
|
||||
this,
|
||||
pointer,
|
||||
VisualRoot,
|
||||
rootVisualPosition,
|
||||
(ulong)Environment.TickCount64,
|
||||
properties,
|
||||
KeyModifiers.None);
|
||||
|
||||
break;
|
||||
}
|
||||
case WindowsMessages.LBUTTONUP:
|
||||
case WindowsMessages.RBUTTONUP:
|
||||
{
|
||||
bool isLeft = msg == WindowsMessages.LBUTTONUP;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
|
||||
RaiseEvent(evnt);
|
||||
|
||||
var evnt = new PointerReleasedEventArgs(
|
||||
this,
|
||||
pointer,
|
||||
VisualRoot,
|
||||
rootVisualPosition,
|
||||
(ulong)Environment.TickCount64,
|
||||
properties,
|
||||
KeyModifiers.None,
|
||||
isLeft ? MouseButton.Left : MouseButton.Right);
|
||||
break;
|
||||
}
|
||||
case WindowsMessages.LBUTTONUP:
|
||||
case WindowsMessages.RBUTTONUP:
|
||||
{
|
||||
bool isLeft = msg == WindowsMessages.LBUTTONUP;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
|
||||
|
||||
RaiseEvent(evnt);
|
||||
var evnt = new PointerReleasedEventArgs(
|
||||
this,
|
||||
pointer,
|
||||
VisualRoot,
|
||||
rootVisualPosition,
|
||||
(ulong)Environment.TickCount64,
|
||||
properties,
|
||||
KeyModifiers.None,
|
||||
isLeft ? MouseButton.Left : MouseButton.Right);
|
||||
|
||||
break;
|
||||
}
|
||||
case WindowsMessages.MOUSEMOVE:
|
||||
{
|
||||
var evnt = new PointerEventArgs(
|
||||
PointerMovedEvent,
|
||||
this,
|
||||
pointer,
|
||||
VisualRoot,
|
||||
rootVisualPosition,
|
||||
(ulong)Environment.TickCount64,
|
||||
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
|
||||
KeyModifiers.None);
|
||||
RaiseEvent(evnt);
|
||||
|
||||
RaiseEvent(evnt);
|
||||
break;
|
||||
}
|
||||
case WindowsMessages.MOUSEMOVE:
|
||||
{
|
||||
var evnt = new PointerEventArgs(
|
||||
PointerMovedEvent,
|
||||
this,
|
||||
pointer,
|
||||
VisualRoot,
|
||||
rootVisualPosition,
|
||||
(ulong)Environment.TickCount64,
|
||||
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
|
||||
KeyModifiers.None);
|
||||
|
||||
break;
|
||||
}
|
||||
RaiseEvent(evnt);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
using System.Text;
|
||||
using Path = System.IO.Path;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels;
|
||||
|
||||
@@ -90,6 +91,8 @@ public class TitleUpdateViewModel : BaseModel
|
||||
Selected = "",
|
||||
Paths = new List<string>()
|
||||
};
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
LoadUpdates();
|
||||
@@ -102,6 +105,9 @@ public class TitleUpdateViewModel : BaseModel
|
||||
AddUpdate(path);
|
||||
}
|
||||
|
||||
// NOTE: Save the list again to remove leftovers.
|
||||
Save();
|
||||
|
||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
||||
|
||||
SelectedUpdate = selected;
|
||||
@@ -223,4 +229,22 @@ public class TitleUpdateViewModel : BaseModel
|
||||
|
||||
SortUpdates();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_titleUpdateWindowData.Paths.Clear();
|
||||
_titleUpdateWindowData.Selected = "";
|
||||
|
||||
foreach (TitleUpdateModel update in TitleUpdates)
|
||||
{
|
||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||
|
||||
if (update == SelectedUpdate)
|
||||
{
|
||||
_titleUpdateWindowData.Selected = update.Path;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
||||
}
|
||||
}
|
||||
@@ -60,24 +60,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public void Save(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel._titleUpdateWindowData.Paths.Clear();
|
||||
|
||||
ViewModel._titleUpdateWindowData.Selected = "";
|
||||
|
||||
foreach (TitleUpdateModel update in ViewModel.TitleUpdates)
|
||||
{
|
||||
ViewModel._titleUpdateWindowData.Paths.Add(update.Path);
|
||||
|
||||
if (update == ViewModel.SelectedUpdate)
|
||||
{
|
||||
ViewModel._titleUpdateWindowData.Selected = update.Path;
|
||||
}
|
||||
}
|
||||
|
||||
using (FileStream titleUpdateJsonStream = File.Create(ViewModel._titleUpdateJsonPath, 4096, FileOptions.WriteThrough))
|
||||
{
|
||||
titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(ViewModel._titleUpdateWindowData, true)));
|
||||
}
|
||||
ViewModel.Save();
|
||||
|
||||
if (VisualRoot is MainWindow window)
|
||||
{
|
||||
|
||||
@@ -349,6 +349,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
||||
return;
|
||||
}
|
||||
|
||||
if (srcTexture.Info.Samples > 1 || dstTexture.Info.Samples > 1)
|
||||
{
|
||||
srcTexture.PropagateScale(dstTexture);
|
||||
}
|
||||
|
||||
float scale = srcTexture.ScaleFactor;
|
||||
float dstScale = dstTexture.ScaleFactor;
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
// This method uses much more memory so we want to avoid it if possible.
|
||||
private const int ByteComparisonSwitchThreshold = 4;
|
||||
|
||||
// Tuning for blacklisting textures from scaling when their data is updated from CPU.
|
||||
// Each write adds the weight, each GPU modification subtracts 1.
|
||||
// Exceeding the threshold blacklists the texture.
|
||||
private const int ScaledSetWeight = 10;
|
||||
private const int ScaledSetThreshold = 30;
|
||||
|
||||
private const int MinLevelsForForceAnisotropy = 5;
|
||||
|
||||
private struct TexturePoolOwner
|
||||
@@ -122,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
private Target _arrayViewTarget;
|
||||
|
||||
private ITexture _flushHostTexture;
|
||||
private ITexture _setHostTexture;
|
||||
private int _scaledSetScore;
|
||||
|
||||
private Texture _viewStorage;
|
||||
|
||||
@@ -518,6 +526,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers when a texture has had its data set after being scaled, and
|
||||
/// determines if it should be blacklisted from scaling to improve performance.
|
||||
/// </summary>
|
||||
/// <returns>True if setting data for a scaled texture is allowed, false if the texture has been blacklisted</returns>
|
||||
private bool AllowScaledSetData()
|
||||
{
|
||||
_scaledSetScore += ScaledSetWeight;
|
||||
|
||||
if (_scaledSetScore >= ScaledSetThreshold)
|
||||
{
|
||||
BlacklistScale();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
|
||||
/// </summary>
|
||||
@@ -554,9 +581,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
|
||||
/// </summary>
|
||||
/// <param name="scale">Scale factor</param>
|
||||
/// <param name="copy">True if the data should be copied to the texture, false otherwise</param>
|
||||
/// <param name="storage">Texture to use instead of creating one</param>
|
||||
/// <returns>A host texture containing a scaled version of this texture</returns>
|
||||
private ITexture GetScaledHostTexture(float scale, ITexture storage = null)
|
||||
private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null)
|
||||
{
|
||||
if (storage == null)
|
||||
{
|
||||
@@ -564,7 +592,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
storage = _context.Renderer.CreateTexture(createInfo, scale);
|
||||
}
|
||||
|
||||
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
|
||||
if (copy)
|
||||
{
|
||||
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
@@ -595,7 +626,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
ScaleFactor = scale;
|
||||
|
||||
ITexture newStorage = GetScaledHostTexture(ScaleFactor);
|
||||
ITexture newStorage = GetScaledHostTexture(ScaleFactor, true);
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");
|
||||
|
||||
@@ -692,11 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public void SynchronizeFull()
|
||||
{
|
||||
if (_hasData)
|
||||
{
|
||||
BlacklistScale();
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
|
||||
|
||||
// If the host does not support ASTC compression, we need to do the decompression.
|
||||
@@ -723,7 +749,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
|
||||
|
||||
HostTexture.SetData(result);
|
||||
if (ScaleFactor != 1f && AllowScaledSetData())
|
||||
{
|
||||
// If needed, create a texture to load from 1x scale.
|
||||
ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture);
|
||||
|
||||
texture.SetData(result);
|
||||
|
||||
texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
HostTexture.SetData(result);
|
||||
}
|
||||
|
||||
_hasData = true;
|
||||
}
|
||||
@@ -1056,7 +1094,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
if (ScaleFactor != 1f)
|
||||
{
|
||||
// If needed, create a texture to flush back to host at 1x scale.
|
||||
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
|
||||
texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
@@ -1456,6 +1494,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public void SignalModified()
|
||||
{
|
||||
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
||||
|
||||
if (_modifiedStale || Group.HasCopyDependencies)
|
||||
{
|
||||
_modifiedStale = false;
|
||||
@@ -1472,6 +1512,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
|
||||
public void SignalModifying(bool bound)
|
||||
{
|
||||
if (bound)
|
||||
{
|
||||
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
||||
}
|
||||
|
||||
if (_modifiedStale || Group.HasCopyDependencies)
|
||||
{
|
||||
_modifiedStale = false;
|
||||
@@ -1685,6 +1730,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
_flushHostTexture?.Release();
|
||||
_flushHostTexture = null;
|
||||
|
||||
_setHostTexture?.Release();
|
||||
_setHostTexture = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <returns>True if eligible</returns>
|
||||
private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale)
|
||||
{
|
||||
if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed)
|
||||
if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed)
|
||||
{
|
||||
return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired;
|
||||
}
|
||||
|
||||
46
Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs
Normal file
46
Ryujinx.Tests/Cpu/Arm64CodeGenCommonTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using ARMeilleure.CodeGen.Arm64;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Ryujinx.Tests.Cpu
|
||||
{
|
||||
public class Arm64CodeGenCommonTests
|
||||
{
|
||||
public struct TestCase
|
||||
{
|
||||
public ulong Value;
|
||||
public bool Valid;
|
||||
public int ImmN;
|
||||
public int ImmS;
|
||||
public int ImmR;
|
||||
}
|
||||
|
||||
public static readonly TestCase[] TestCases =
|
||||
{
|
||||
new() { Value = 0, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
|
||||
new() { Value = 0x970977f35f848714, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
|
||||
new() { Value = 0xffffffffffffffff, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
|
||||
new() { Value = 0x5555555555555555, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 0 },
|
||||
new() { Value = 0xaaaaaaaaaaaaaaaa, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 1 },
|
||||
new() { Value = 0x6666666666666666, Valid = true, ImmN = 0, ImmS = 0x39, ImmR = 3 },
|
||||
new() { Value = 0x1c1c1c1c1c1c1c1c, Valid = true, ImmN = 0, ImmS = 0x32, ImmR = 6 },
|
||||
new() { Value = 0x0f0f0f0f0f0f0f0f, Valid = true, ImmN = 0, ImmS = 0x33, ImmR = 0 },
|
||||
new() { Value = 0xf1f1f1f1f1f1f1f1, Valid = true, ImmN = 0, ImmS = 0x34, ImmR = 4 },
|
||||
new() { Value = 0xe7e7e7e7e7e7e7e7, Valid = true, ImmN = 0, ImmS = 0x35, ImmR = 3 },
|
||||
new() { Value = 0xc001c001c001c001, Valid = true, ImmN = 0, ImmS = 0x22, ImmR = 2 },
|
||||
new() { Value = 0x0000038000000380, Valid = true, ImmN = 0, ImmS = 0x02, ImmR = 25 },
|
||||
new() { Value = 0xffff8fffffff8fff, Valid = true, ImmN = 0, ImmS = 0x1c, ImmR = 17 },
|
||||
new() { Value = 0x000000000ffff800, Valid = true, ImmN = 1, ImmS = 0x10, ImmR = 53 },
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void BitImmTests([ValueSource(nameof(TestCases))] TestCase test)
|
||||
{
|
||||
bool valid = CodeGenCommon.TryEncodeBitMask(test.Value, out int immN, out int immS, out int immR);
|
||||
|
||||
Assert.That(valid, Is.EqualTo(test.Valid));
|
||||
Assert.That(immN, Is.EqualTo(test.ImmN));
|
||||
Assert.That(immS, Is.EqualTo(test.ImmS));
|
||||
Assert.That(immR, Is.EqualTo(test.ImmR));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user