Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
efb135b74c | ||
|
a707842e14 | ||
|
a5a9b9bc8b | ||
|
17078ad929 | ||
|
32450d45de | ||
|
ed7a0474c6 | ||
|
fe9c49949a | ||
|
052b23c83c | ||
|
e4f68592c3 | ||
|
1dcd44b94f | ||
|
61b1ce252f | ||
|
5f38086f94 | ||
|
7bae440d3a | ||
|
f1943fd0b6 | ||
|
ec8d4f3af5 | ||
|
b3f0978869 | ||
|
f614d2c435 | ||
|
40c9416097 | ||
|
618c8edc79 | ||
|
99fc4fa61b | ||
|
f6d5499a16 | ||
|
26bf13a65d | ||
|
96cf242bcf | ||
|
59755818ef | ||
|
f8beeeb7d3 |
57
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
57
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,31 +1,27 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: File a bug report
|
description: File a bug report
|
||||||
|
title: "[Bug]"
|
||||||
|
labels: bug
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search to see if an issue already exists for the bug you encountered.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: issue
|
id: issue
|
||||||
attributes:
|
attributes:
|
||||||
label: Description of Issue
|
label: Description of the issue
|
||||||
description: What's the issue you encountered?
|
description: What's the issue you encountered?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: repro
|
id: repro
|
||||||
attributes:
|
attributes:
|
||||||
label: Reproduction Steps
|
label: Reproduction steps
|
||||||
description: How can the issue be reproduced?
|
description: How can the issue be reproduced?
|
||||||
|
placeholder: Describe each step as precisely as possible
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: log
|
id: log
|
||||||
attributes:
|
attributes:
|
||||||
label: Log File
|
label: Log file
|
||||||
description: A log file will help our developers to better diagnose and fix the issue.
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
||||||
validations:
|
validations:
|
||||||
@@ -34,36 +30,51 @@ body:
|
|||||||
id: os
|
id: os
|
||||||
attributes:
|
attributes:
|
||||||
label: OS
|
label: OS
|
||||||
placeholder: "Example: Windows 10"
|
placeholder: "e.g. Windows 10"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: ryujinx-version
|
id: ryujinx-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Ryujinx version
|
label: Ryujinx version
|
||||||
placeholder: |
|
placeholder: "e.g. 1.0.470"
|
||||||
- *(e.g. 1.0.470)*
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: game-version
|
id: game-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Game version
|
label: Game version
|
||||||
placeholder: |
|
placeholder: "e.g. 1.1.1"
|
||||||
- *(e.g. 1.1.1)*
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: cpu
|
||||||
|
attributes:
|
||||||
|
label: CPU
|
||||||
|
placeholder: "e.g. i7-6700"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: gpu
|
||||||
|
attributes:
|
||||||
|
label: GPU
|
||||||
|
placeholder: "e.g. NVIDIA RTX 2070"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: ram
|
||||||
|
attributes:
|
||||||
|
label: RAM
|
||||||
|
placeholder: "e.g. 16GB"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: environment
|
id: mods
|
||||||
attributes:
|
attributes:
|
||||||
label: Environment?
|
label: List of applied mods
|
||||||
value: |
|
placeholder: You can list applied mods here.
|
||||||
- ##### CPU: *(e.g. i7-6700)*
|
|
||||||
- ##### GPU: *(e.g. NVIDIA RTX 2070)*
|
|
||||||
- ##### RAM: *(e.g. 16GB)*
|
|
||||||
- Applied Mods: [ Yes (Which ones) / No ]
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-context
|
id: additional-context
|
||||||
attributes:
|
attributes:
|
||||||
|
12
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
12
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,13 +1,7 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest a new feature for Ryujinx.
|
description: Suggest a new feature for Ryujinx.
|
||||||
|
title: "[Feature Request]"
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search to see if an issue already exists for the feature you are requesting.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: overview
|
id: overview
|
||||||
attributes:
|
attributes:
|
||||||
@@ -18,14 +12,14 @@ body:
|
|||||||
- type: textarea
|
- type: textarea
|
||||||
id: details
|
id: details
|
||||||
attributes:
|
attributes:
|
||||||
label: Smaller Details
|
label: Smaller details
|
||||||
description: These may include specific methods of implementation etc.
|
description: These may include specific methods of implementation etc.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: request
|
id: request
|
||||||
attributes:
|
attributes:
|
||||||
label: Nature of Request
|
label: Nature of request
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
@@ -1,13 +1,8 @@
|
|||||||
name: Missing CPU Instruction
|
name: Missing CPU Instruction
|
||||||
description: CPU Instruction is missing in Ryujinx.
|
description: CPU Instruction is missing in Ryujinx.
|
||||||
|
title: "[CPU]"
|
||||||
|
labels: [cpu, not-implemented]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search for existing missing CPU instruction
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: instruction
|
id: instruction
|
||||||
attributes:
|
attributes:
|
||||||
|
10
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
10
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
@@ -1,17 +1,11 @@
|
|||||||
name: Missing Service Call
|
name: Missing Service Call
|
||||||
description: Service call is missing in Ryujinx.
|
description: Service call is missing in Ryujinx.
|
||||||
|
labels: not-implemented
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search for an [existing missing service call issue](https://github.com/Ryujinx/Ryujinx/issues) first.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: instruction
|
id: instruction
|
||||||
attributes:
|
attributes:
|
||||||
label: Service Call
|
label: Service call
|
||||||
description: What service call is missing?
|
description: What service call is missing?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
@@ -71,6 +71,7 @@ namespace ARMeilleure.Memory
|
|||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="write">True if the region was written, false if read</param>
|
/// <param name="write">True if the region was written, false if read</param>
|
||||||
/// <param name="precise">True if the access is precise, false otherwise</param>
|
/// <param name="precise">True if the access is precise, false otherwise</param>
|
||||||
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false);
|
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
|
||||||
|
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -222,7 +222,7 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
// Tracking action should be non-null to call it, otherwise assume false return.
|
// Tracking action should be non-null to call it, otherwise assume false return.
|
||||||
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
||||||
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite, Const(0));
|
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite);
|
||||||
context.Copy(inRegionLocal, result);
|
context.Copy(inRegionLocal, result);
|
||||||
|
|
||||||
context.MarkLabel(skipActionLabel);
|
context.MarkLabel(skipActionLabel);
|
||||||
|
@@ -44,11 +44,12 @@
|
|||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.1" />
|
||||||
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
||||||
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
|
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||||
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-9c9356d" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -53,7 +53,6 @@ using Key = Ryujinx.Input.Key;
|
|||||||
using MouseButton = Ryujinx.Input.MouseButton;
|
using MouseButton = Ryujinx.Input.MouseButton;
|
||||||
using Size = Avalonia.Size;
|
using Size = Avalonia.Size;
|
||||||
using Switch = Ryujinx.HLE.Switch;
|
using Switch = Ryujinx.HLE.Switch;
|
||||||
using WindowState = Avalonia.Controls.WindowState;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
@@ -766,7 +765,7 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void RenderLoop()
|
private void RenderLoop()
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
@@ -802,6 +801,8 @@ namespace Ryujinx.Ava
|
|||||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||||
Translator.IsReadyForTranslation.Set();
|
Translator.IsReadyForTranslation.Set();
|
||||||
|
|
||||||
|
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||||
|
|
||||||
while (_isActive)
|
while (_isActive)
|
||||||
{
|
{
|
||||||
_ticks += _chrono.ElapsedTicks;
|
_ticks += _chrono.ElapsedTicks;
|
||||||
|
@@ -132,8 +132,8 @@ namespace Ryujinx.Modules
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If build not done, assume no new update are availaible.
|
// If build not done, assume no new update are available.
|
||||||
if (_buildUrl == null)
|
if (_buildUrl is null)
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
@@ -240,13 +240,13 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
HttpClient result = new();
|
HttpClient result = new();
|
||||||
|
|
||||||
// Required by GitHub to interract with APIs.
|
// Required by GitHub to interact with APIs.
|
||||||
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
|
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void UpdateRyujinx(Window parent, string downloadUrl)
|
private static async void UpdateRyujinx(Window parent, string downloadUrl)
|
||||||
{
|
{
|
||||||
_updateSuccessful = false;
|
_updateSuccessful = false;
|
||||||
|
|
||||||
@@ -300,8 +300,6 @@ namespace Ryujinx.Modules
|
|||||||
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFileExecutable(ryuExe);
|
|
||||||
|
|
||||||
Process.Start(ryuExe, CommandLineState.Arguments);
|
Process.Start(ryuExe, CommandLineState.Arguments);
|
||||||
|
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
@@ -408,9 +406,9 @@ namespace Ryujinx.Modules
|
|||||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||||
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||||
|
|
||||||
for (int j = 0; j < webClients.Count; j++)
|
foreach (WebClient webClient in webClients)
|
||||||
{
|
{
|
||||||
webClients[j].CancelAsync();
|
webClient.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||||
@@ -472,22 +470,6 @@ namespace Ryujinx.Modules
|
|||||||
worker.Start();
|
worker.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetFileExecutable(string path)
|
|
||||||
{
|
|
||||||
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
|
|
||||||
UnixFileMode.UserWrite |
|
|
||||||
UnixFileMode.UserRead |
|
|
||||||
UnixFileMode.GroupRead |
|
|
||||||
UnixFileMode.GroupWrite |
|
|
||||||
UnixFileMode.OtherRead |
|
|
||||||
UnixFileMode.OtherWrite;
|
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && File.Exists(path))
|
|
||||||
{
|
|
||||||
File.SetUnixFileMode(path, ExecutableFileMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||||
{
|
{
|
||||||
// Extract Update
|
// Extract Update
|
||||||
@@ -503,7 +485,10 @@ namespace Ryujinx.Modules
|
|||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
TarEntry tarEntry;
|
TarEntry tarEntry;
|
||||||
while ((tarEntry = tarStream.GetNextEntry()) != null)
|
|
||||||
|
if (!OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
||||||
{
|
{
|
||||||
if (tarEntry.IsDirectory) continue;
|
if (tarEntry.IsDirectory) continue;
|
||||||
|
|
||||||
@@ -516,15 +501,15 @@ namespace Ryujinx.Modules
|
|||||||
tarStream.CopyEntryContents(outStream);
|
tarStream.CopyEntryContents(outStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
|
||||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
||||||
|
|
||||||
TarEntry entry = tarEntry;
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
||||||
@@ -603,8 +588,6 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
Directory.Delete(UpdateDir, true);
|
Directory.Delete(UpdateDir, true);
|
||||||
|
|
||||||
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
|
|
||||||
|
|
||||||
_updateSuccessful = true;
|
_updateSuccessful = true;
|
||||||
|
|
||||||
taskDialog.Hide();
|
taskDialog.Hide();
|
||||||
|
@@ -1,127 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Avalonia;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Helpers
|
|
||||||
{
|
|
||||||
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
static partial class MetalHelper
|
|
||||||
{
|
|
||||||
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
|
|
||||||
|
|
||||||
private struct Selector
|
|
||||||
{
|
|
||||||
public readonly IntPtr NativePtr;
|
|
||||||
|
|
||||||
public unsafe Selector(string value)
|
|
||||||
{
|
|
||||||
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
|
|
||||||
byte* data = stackalloc byte[size];
|
|
||||||
|
|
||||||
fixed (char* pValue = value)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
NativePtr = sel_registerName(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator Selector(string value) => new Selector(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe IntPtr GetClass(string value)
|
|
||||||
{
|
|
||||||
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
|
|
||||||
byte* data = stackalloc byte[size];
|
|
||||||
|
|
||||||
fixed (char* pValue = value)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objc_getClass(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct NSPoint
|
|
||||||
{
|
|
||||||
public double X;
|
|
||||||
public double Y;
|
|
||||||
|
|
||||||
public NSPoint(double x, double y)
|
|
||||||
{
|
|
||||||
X = x;
|
|
||||||
Y = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct NSRect
|
|
||||||
{
|
|
||||||
public NSPoint Pos;
|
|
||||||
public NSPoint Size;
|
|
||||||
|
|
||||||
public NSRect(double x, double y, double width, double height)
|
|
||||||
{
|
|
||||||
Pos = new NSPoint(x, y);
|
|
||||||
Size = new NSPoint(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetMetalLayer(out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
|
|
||||||
{
|
|
||||||
// Create a new CAMetalLayer.
|
|
||||||
IntPtr layerClass = GetClass("CAMetalLayer");
|
|
||||||
IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
|
|
||||||
objc_msgSend(metalLayer, "init");
|
|
||||||
|
|
||||||
// Create a child NSView to render into.
|
|
||||||
IntPtr nsViewClass = GetClass("NSView");
|
|
||||||
IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
|
|
||||||
objc_msgSend(child, "init", new NSRect(0, 0, 0, 0));
|
|
||||||
|
|
||||||
// Make its renderer our metal layer.
|
|
||||||
objc_msgSend(child, "setWantsLayer:", (byte)1);
|
|
||||||
objc_msgSend(child, "setLayer:", metalLayer);
|
|
||||||
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
|
||||||
|
|
||||||
// Ensure the scale factor is up to date.
|
|
||||||
updateBounds = (Rect rect) => {
|
|
||||||
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
|
||||||
};
|
|
||||||
|
|
||||||
nsView = child;
|
|
||||||
return metalLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DestroyMetalLayer(IntPtr nsView, IntPtr metalLayer)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static unsafe partial IntPtr sel_registerName(byte* data);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static unsafe partial IntPtr objc_getClass(byte* data);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
|
|
||||||
private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,9 +2,9 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using SPB.Graphics;
|
using SPB.Graphics;
|
||||||
using SPB.Platform;
|
using SPB.Platform;
|
||||||
using SPB.Platform.GLX;
|
using SPB.Platform.GLX;
|
||||||
@@ -30,6 +30,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
protected IntPtr NsView { get; set; }
|
protected IntPtr NsView { get; set; }
|
||||||
protected IntPtr MetalLayer { get; set; }
|
protected IntPtr MetalLayer { get; set; }
|
||||||
|
|
||||||
|
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
||||||
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
||||||
|
|
||||||
public event EventHandler<IntPtr> WindowCreated;
|
public event EventHandler<IntPtr> WindowCreated;
|
||||||
@@ -237,8 +238,29 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
IPlatformHandle CreateMacOS()
|
IPlatformHandle CreateMacOS()
|
||||||
{
|
{
|
||||||
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
|
// Create a new CAMetalLayer.
|
||||||
|
IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer");
|
||||||
|
IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc");
|
||||||
|
ObjectiveC.objc_msgSend(metalLayer, "init");
|
||||||
|
|
||||||
|
// Create a child NSView to render into.
|
||||||
|
IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView");
|
||||||
|
IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc");
|
||||||
|
ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0));
|
||||||
|
|
||||||
|
// Make its renderer our metal layer.
|
||||||
|
ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1);
|
||||||
|
ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer);
|
||||||
|
ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
||||||
|
|
||||||
|
// Ensure the scale factor is up to date.
|
||||||
|
_updateBoundsCallback = rect =>
|
||||||
|
{
|
||||||
|
ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
||||||
|
};
|
||||||
|
|
||||||
|
IntPtr nsView = child;
|
||||||
|
MetalLayer = metalLayer;
|
||||||
NsView = nsView;
|
NsView = nsView;
|
||||||
|
|
||||||
return new PlatformHandle(nsView, "NSView");
|
return new PlatformHandle(nsView, "NSView");
|
||||||
@@ -260,7 +282,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
void DestroyMacOS()
|
void DestroyMacOS()
|
||||||
{
|
{
|
||||||
MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -634,13 +634,13 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false)
|
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
|
|
||||||
if (precise)
|
if (precise)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write, precise: true);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +663,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
if (state >= tag)
|
if (state >= tag)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (state == 0)
|
else if (state == 0)
|
||||||
@@ -706,7 +706,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
// Only trigger tracking from reads if both bits are set on any page.
|
// Only trigger tracking from reads if both bits are set on any page.
|
||||||
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
|
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -822,21 +822,21 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuRegionHandle BeginTracking(ulong address, ulong size)
|
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
|
||||||
{
|
{
|
||||||
return new CpuRegionHandle(Tracking.BeginTracking(address, size));
|
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity));
|
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity)
|
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity));
|
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -28,8 +28,9 @@ namespace Ryujinx.Cpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
CpuRegionHandle BeginTracking(ulong address, ulong size);
|
CpuRegionHandle BeginTracking(ulong address, ulong size, int id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
|
/// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
|
||||||
@@ -38,8 +39,9 @@ namespace Ryujinx.Cpu
|
|||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
|
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
|
||||||
/// <param name="granularity">Desired granularity of write tracking</param>
|
/// <param name="granularity">Desired granularity of write tracking</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity);
|
CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
|
/// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
|
||||||
@@ -47,7 +49,8 @@ namespace Ryujinx.Cpu
|
|||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="granularity">Desired granularity of write tracking</param>
|
/// <param name="granularity">Desired granularity of write tracking</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity);
|
CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -629,31 +629,31 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuRegionHandle BeginTracking(ulong address, ulong size)
|
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
|
||||||
{
|
{
|
||||||
return new CpuRegionHandle(Tracking.BeginTracking(address, size));
|
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity));
|
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity)
|
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity));
|
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false)
|
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
|
|
||||||
if (precise)
|
if (precise)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write, precise: true);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,7 +676,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
|
|
||||||
if ((pte & tag) != 0)
|
if ((pte & tag) != 0)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -518,13 +518,13 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false)
|
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||||
{
|
{
|
||||||
AssertValidAddressAndSize(va, size);
|
AssertValidAddressAndSize(va, size);
|
||||||
|
|
||||||
if (precise)
|
if (precise)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write, precise: true);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +547,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
|
|
||||||
if (state >= tag)
|
if (state >= tag)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (state == 0)
|
else if (state == 0)
|
||||||
@@ -590,7 +590,7 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
// Only trigger tracking from reads if both bits are set on any page.
|
// Only trigger tracking from reads if both bits are set on any page.
|
||||||
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
|
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
|
||||||
{
|
{
|
||||||
Tracking.VirtualMemoryEvent(va, size, write);
|
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -706,21 +706,21 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuRegionHandle BeginTracking(ulong address, ulong size)
|
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
|
||||||
{
|
{
|
||||||
return new CpuRegionHandle(Tracking.BeginTracking(address, size));
|
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity));
|
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity)
|
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity));
|
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Cpu
|
|||||||
{
|
{
|
||||||
public class MemoryEhMeilleure : IDisposable
|
public class MemoryEhMeilleure : IDisposable
|
||||||
{
|
{
|
||||||
private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write, bool precise = false);
|
private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write);
|
||||||
|
|
||||||
private readonly MemoryTracking _tracking;
|
private readonly MemoryTracking _tracking;
|
||||||
private readonly TrackingEventDelegate _trackingEvent;
|
private readonly TrackingEventDelegate _trackingEvent;
|
||||||
|
@@ -725,10 +725,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool clearDepth = (argument & 1) != 0;
|
||||||
|
bool clearStencil = (argument & 2) != 0;
|
||||||
|
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||||
int index = (argument >> 6) & 0xf;
|
int index = (argument >> 6) & 0xf;
|
||||||
int layer = (argument >> 10) & 0x3ff;
|
int layer = (argument >> 10) & 0x3ff;
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: false, layered: layer != 0 || layerCount > 1, singleUse: index);
|
RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor;
|
||||||
|
|
||||||
|
if (layer != 0 || layerCount > 1)
|
||||||
|
{
|
||||||
|
updateFlags |= RenderTargetUpdateFlags.Layered;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearDepth || clearStencil)
|
||||||
|
{
|
||||||
|
updateFlags |= RenderTargetUpdateFlags.UpdateDepthStencil;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.UpdateRenderTargetState(updateFlags, singleUse: componentMask != 0 ? index : -1);
|
||||||
|
|
||||||
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
||||||
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
||||||
@@ -788,18 +803,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_context.Renderer.Pipeline.SetScissors(scissors);
|
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clipMismatch)
|
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTarget(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTargets();
|
_channel.TextureManager.UpdateRenderTargets();
|
||||||
}
|
|
||||||
|
|
||||||
bool clearDepth = (argument & 1) != 0;
|
|
||||||
bool clearStencil = (argument & 2) != 0;
|
|
||||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
|
||||||
|
|
||||||
if (componentMask != 0)
|
if (componentMask != 0)
|
||||||
{
|
{
|
||||||
@@ -841,7 +845,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
engine.UpdateScissorState();
|
engine.UpdateScissorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: true);
|
engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||||
|
|
||||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Flags indicating how the render targets should be updated.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
enum RenderTargetUpdateFlags
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No flags.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get render target index from the control register.
|
||||||
|
/// </summary>
|
||||||
|
UseControl = 1 << 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that all render targets are 2D array textures.
|
||||||
|
/// </summary>
|
||||||
|
Layered = 1 << 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that only a single color target will be used.
|
||||||
|
/// </summary>
|
||||||
|
SingleColor = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the depth-stencil target will be used.
|
||||||
|
/// </summary>
|
||||||
|
UpdateDepthStencil = 1 << 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default update flags for draw.
|
||||||
|
/// </summary>
|
||||||
|
UpdateAll = UseControl | UpdateDepthStencil
|
||||||
|
}
|
||||||
|
}
|
@@ -402,20 +402,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateRenderTargetState()
|
private void UpdateRenderTargetState()
|
||||||
{
|
{
|
||||||
UpdateRenderTargetState(true);
|
UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||||
/// <param name="layered">Indicates if the texture is layered</param>
|
|
||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
var rtControl = _state.State.RtControl;
|
var rtControl = _state.State.RtControl;
|
||||||
|
|
||||||
|
bool useControl = updateFlags.HasFlag(RenderTargetUpdateFlags.UseControl);
|
||||||
|
bool layered = updateFlags.HasFlag(RenderTargetUpdateFlags.Layered);
|
||||||
|
bool singleColor = updateFlags.HasFlag(RenderTargetUpdateFlags.SingleColor);
|
||||||
|
|
||||||
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
||||||
|
|
||||||
var msaaMode = _state.State.RtMsaaMode;
|
var msaaMode = _state.State.RtMsaaMode;
|
||||||
@@ -438,7 +441,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
var colorState = _state.State.RtColorState[rtIndex];
|
var colorState = _state.State.RtColorState[rtIndex];
|
||||||
|
|
||||||
if (index >= count || !IsRtEnabled(colorState))
|
if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse))
|
||||||
{
|
{
|
||||||
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
|
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
|
||||||
|
|
||||||
@@ -478,7 +481,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
Image.Texture depthStencil = null;
|
Image.Texture depthStencil = null;
|
||||||
|
|
||||||
if (dsEnable)
|
if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil))
|
||||||
{
|
{
|
||||||
var dsState = _state.State.RtDepthStencilState;
|
var dsState = _state.State.RtDepthStencilState;
|
||||||
var dsSize = _state.State.RtDepthStencilSize;
|
var dsSize = _state.State.RtDepthStencilSize;
|
||||||
|
@@ -139,12 +139,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||||
/// <param name="layered">Indicates if the texture is layered</param>
|
|
||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
|
||||||
{
|
{
|
||||||
_stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse);
|
_stateUpdater.UpdateRenderTargetState(updateFlags, singleUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -33,10 +33,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class AutoDeleteCache : IEnumerable<Texture>
|
class AutoDeleteCache : IEnumerable<Texture>
|
||||||
{
|
{
|
||||||
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
|
private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
private ulong _totalSize;
|
||||||
|
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCache;
|
private HashSet<ShortTextureCacheEntry> _shortCache;
|
||||||
@@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public AutoDeleteCache()
|
public AutoDeleteCache()
|
||||||
{
|
{
|
||||||
_textures = new LinkedList<Texture>();
|
_textures = new LinkedList<Texture>();
|
||||||
_deferredRemovals = new ConcurrentQueue<Texture>();
|
|
||||||
|
|
||||||
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
||||||
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
||||||
@@ -67,37 +68,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="texture">The texture to be added to the cache</param>
|
/// <param name="texture">The texture to be added to the cache</param>
|
||||||
public void Add(Texture texture)
|
public void Add(Texture texture)
|
||||||
{
|
{
|
||||||
texture.IncrementReferenceCount();
|
_totalSize += texture.Size;
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
if (_textures.Count > MaxCapacity)
|
if (_textures.Count > MaxCapacity ||
|
||||||
|
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||||
{
|
{
|
||||||
Texture oldestTexture = _textures.First.Value;
|
RemoveLeastUsedTexture();
|
||||||
|
|
||||||
if (!oldestTexture.CheckModified(false))
|
|
||||||
{
|
|
||||||
// The texture must be flushed if it falls out of the auto delete cache.
|
|
||||||
// Flushes out of the auto delete cache do not trigger write tracking,
|
|
||||||
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
|
||||||
|
|
||||||
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
|
||||||
oldestTexture.FlushModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_textures.RemoveFirst();
|
|
||||||
|
|
||||||
oldestTexture.DecrementReferenceCount();
|
|
||||||
|
|
||||||
oldestTexture.CacheNode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_deferredRemovals.Count > 0)
|
|
||||||
{
|
|
||||||
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
|
|
||||||
{
|
|
||||||
Remove(textureToRemove, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||||
|
{
|
||||||
|
RemoveLeastUsedTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -127,6 +111,31 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the least used texture from the cache.
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveLeastUsedTexture()
|
||||||
|
{
|
||||||
|
Texture oldestTexture = _textures.First.Value;
|
||||||
|
|
||||||
|
_totalSize -= oldestTexture.Size;
|
||||||
|
|
||||||
|
if (!oldestTexture.CheckModified(false))
|
||||||
|
{
|
||||||
|
// The texture must be flushed if it falls out of the auto delete cache.
|
||||||
|
// Flushes out of the auto delete cache do not trigger write tracking,
|
||||||
|
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
||||||
|
|
||||||
|
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
||||||
|
oldestTexture.FlushModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textures.RemoveFirst();
|
||||||
|
|
||||||
|
oldestTexture.DecrementReferenceCount();
|
||||||
|
oldestTexture.CacheNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a texture from the cache.
|
/// Removes a texture from the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_textures.Remove(texture.CacheNode);
|
_textures.Remove(texture.CacheNode);
|
||||||
|
|
||||||
|
_totalSize -= texture.Size;
|
||||||
|
|
||||||
texture.CacheNode = null;
|
texture.CacheNode = null;
|
||||||
|
|
||||||
return texture.DecrementReferenceCount();
|
return texture.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues removal of a texture from the cache in a thread safe way.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">The texture to be removed from the cache</param>
|
|
||||||
public void RemoveDeferred(Texture texture)
|
|
||||||
{
|
|
||||||
_deferredRemovals.Enqueue(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
||||||
_memoryTracking = physicalMemory.BeginGranularTracking(address, size);
|
_memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool);
|
||||||
_memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
|
_memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
|
||||||
_modifiedDelegate = RegionModified;
|
_modifiedDelegate = RegionModified;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
@@ -89,12 +88,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureGroup Group { get; private set; }
|
public TextureGroup Group { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set when a texture has been changed size. This indicates that it may need to be
|
|
||||||
/// changed again when obtained as a sampler.
|
|
||||||
/// </summary>
|
|
||||||
public bool ChangedSize { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set when a texture's GPU VA has ever been partially or fully unmapped.
|
/// Set when a texture's GPU VA has ever been partially or fully unmapped.
|
||||||
/// This indicates that the range must be fully checked when matching the texture.
|
/// This indicates that the range must be fully checked when matching the texture.
|
||||||
@@ -410,122 +403,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
|
Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the texture size.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This operation may also change the size of all mipmap levels, including from the parent
|
|
||||||
/// and other possible child textures, to ensure that all sizes are consistent.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
public void ChangeSize(int width, int height, int depthOrLayers)
|
|
||||||
{
|
|
||||||
int blockWidth = Info.FormatInfo.BlockWidth;
|
|
||||||
int blockHeight = Info.FormatInfo.BlockHeight;
|
|
||||||
|
|
||||||
width <<= FirstLevel;
|
|
||||||
height <<= FirstLevel;
|
|
||||||
|
|
||||||
if (Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
depthOrLayers <<= FirstLevel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
depthOrLayers = _viewStorage.Info.DepthOrLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
_viewStorage.RecreateStorageOrView(width, height, blockWidth, blockHeight, depthOrLayers);
|
|
||||||
|
|
||||||
foreach (Texture view in _viewStorage._views)
|
|
||||||
{
|
|
||||||
int viewWidth = Math.Max(1, width >> view.FirstLevel);
|
|
||||||
int viewHeight = Math.Max(1, height >> view.FirstLevel);
|
|
||||||
|
|
||||||
int viewDepthOrLayers;
|
|
||||||
|
|
||||||
if (view.Info.Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
viewDepthOrLayers = Math.Max(1, depthOrLayers >> view.FirstLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
viewDepthOrLayers = view.Info.DepthOrLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
view.RecreateStorageOrView(viewWidth, viewHeight, blockWidth, blockHeight, viewDepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Recreates the texture storage (or view, in the case of child textures) of this texture.
|
|
||||||
/// This allows recreating the texture with a new size.
|
|
||||||
/// A copy is automatically performed from the old to the new texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="width">The block width related to the given width</param>
|
|
||||||
/// <param name="height">The block height related to the given height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
private void RecreateStorageOrView(int width, int height, int blockWidth, int blockHeight, int depthOrLayers)
|
|
||||||
{
|
|
||||||
RecreateStorageOrView(
|
|
||||||
BitUtils.DivRoundUp(width * Info.FormatInfo.BlockWidth, blockWidth),
|
|
||||||
BitUtils.DivRoundUp(height * Info.FormatInfo.BlockHeight, blockHeight),
|
|
||||||
depthOrLayers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Recreates the texture storage (or view, in the case of child textures) of this texture.
|
|
||||||
/// This allows recreating the texture with a new size.
|
|
||||||
/// A copy is automatically performed from the old to the new texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
private void RecreateStorageOrView(int width, int height, int depthOrLayers)
|
|
||||||
{
|
|
||||||
ChangedSize = true;
|
|
||||||
|
|
||||||
SetInfo(new TextureInfo(
|
|
||||||
Info.GpuAddress,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depthOrLayers,
|
|
||||||
Info.Levels,
|
|
||||||
Info.SamplesInX,
|
|
||||||
Info.SamplesInY,
|
|
||||||
Info.Stride,
|
|
||||||
Info.IsLinear,
|
|
||||||
Info.GobBlocksInY,
|
|
||||||
Info.GobBlocksInZ,
|
|
||||||
Info.GobBlocksInTileX,
|
|
||||||
Info.Target,
|
|
||||||
Info.FormatInfo,
|
|
||||||
Info.DepthStencilMode,
|
|
||||||
Info.SwizzleR,
|
|
||||||
Info.SwizzleG,
|
|
||||||
Info.SwizzleB,
|
|
||||||
Info.SwizzleA));
|
|
||||||
|
|
||||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
|
|
||||||
|
|
||||||
if (_viewStorage != this)
|
|
||||||
{
|
|
||||||
ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, FirstLayer, FirstLevel));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ITexture newStorage = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
|
|
||||||
|
|
||||||
HostTexture.CopyTo(newStorage, 0, 0);
|
|
||||||
|
|
||||||
ReplaceStorage(newStorage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers when a texture has had its data set after being scaled, and
|
/// Registers when a texture has had its data set after being scaled, and
|
||||||
/// determines if it should be blacklisted from scaling to improve performance.
|
/// determines if it should be blacklisted from scaling to improve performance.
|
||||||
@@ -1215,7 +1092,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>A value indicating how well this texture matches the given info</returns>
|
/// <returns>A value indicating how well this texture matches the given info</returns>
|
||||||
public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
|
public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
|
||||||
{
|
{
|
||||||
TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0);
|
bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
|
||||||
|
|
||||||
|
TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
|
||||||
|
|
||||||
if (matchQuality == TextureMatchQuality.NoMatch)
|
if (matchQuality == TextureMatchQuality.NoMatch)
|
||||||
{
|
{
|
||||||
@@ -1227,12 +1106,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return TextureMatchQuality.NoMatch;
|
return TextureMatchQuality.NoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0, FirstLevel))
|
if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
|
||||||
{
|
{
|
||||||
return TextureMatchQuality.NoMatch;
|
return TextureMatchQuality.NoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0)
|
if ((flags & TextureSearchFlags.ForSampler) != 0)
|
||||||
{
|
{
|
||||||
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
|
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
|
||||||
{
|
{
|
||||||
@@ -1262,12 +1141,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">Texture view information</param>
|
/// <param name="info">Texture view information</param>
|
||||||
/// <param name="range">Texture view physical memory ranges</param>
|
/// <param name="range">Texture view physical memory ranges</param>
|
||||||
|
/// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
|
||||||
/// <param name="layerSize">Layer size on the given texture</param>
|
/// <param name="layerSize">Layer size on the given texture</param>
|
||||||
/// <param name="caps">Host GPU capabilities</param>
|
/// <param name="caps">Host GPU capabilities</param>
|
||||||
/// <param name="firstLayer">Texture view initial layer on this texture</param>
|
/// <param name="firstLayer">Texture view initial layer on this texture</param>
|
||||||
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
|
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
|
||||||
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
|
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
|
||||||
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
|
public TextureViewCompatibility IsViewCompatible(
|
||||||
|
TextureInfo info,
|
||||||
|
MultiRange range,
|
||||||
|
bool exactSize,
|
||||||
|
int layerSize,
|
||||||
|
Capabilities caps,
|
||||||
|
out int firstLayer,
|
||||||
|
out int firstLevel)
|
||||||
{
|
{
|
||||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
|
|
||||||
@@ -1317,7 +1204,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return TextureViewCompatibility.LayoutIncompatible;
|
return TextureViewCompatibility.LayoutIncompatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel));
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
|
||||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -1750,13 +1637,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveFromPools(true);
|
RemoveFromPools(true);
|
||||||
|
|
||||||
// We only want to remove if there's no mapped region of the texture that was modified by the GPU,
|
|
||||||
// otherwise we could lose data.
|
|
||||||
if (!Group.AnyModified(this))
|
|
||||||
{
|
|
||||||
_physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -210,8 +210,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
ulong offset,
|
ulong offset,
|
||||||
FormatInfo formatInfo,
|
FormatInfo formatInfo,
|
||||||
bool shouldCreate,
|
bool shouldCreate,
|
||||||
bool preferScaling = true,
|
bool preferScaling,
|
||||||
Size? sizeHint = null)
|
Size sizeHint)
|
||||||
{
|
{
|
||||||
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
copyTexture.Address.Pack() + offset,
|
copyTexture.Address.Pack() + offset,
|
||||||
width,
|
GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout),
|
||||||
copyTexture.Height,
|
copyTexture.Height,
|
||||||
copyTexture.Depth,
|
copyTexture.Depth,
|
||||||
1,
|
1,
|
||||||
@@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
flags |= TextureSearchFlags.NoCreate;
|
flags |= TextureSearchFlags.NoCreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
colorState.Address.Pack(),
|
colorState.Address.Pack(),
|
||||||
width,
|
GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear),
|
||||||
colorState.Height,
|
colorState.Height,
|
||||||
colorState.Depth,
|
colorState.Depth,
|
||||||
1,
|
1,
|
||||||
@@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
dsState.Address.Pack(),
|
dsState.Address.Pack(),
|
||||||
size.Width,
|
GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false),
|
||||||
size.Height,
|
size.Height,
|
||||||
size.Depth,
|
size.Depth,
|
||||||
1,
|
1,
|
||||||
@@ -409,13 +409,41 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
target,
|
target,
|
||||||
formatInfo);
|
formatInfo);
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For block linear textures, gets the minimum width of the texture
|
||||||
|
/// that would still have the same number of GOBs per row as the original width.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">The possibly aligned texture width</param>
|
||||||
|
/// <param name="minimumWidth">The minimum width that the texture may have without losing data</param>
|
||||||
|
/// <param name="bytesPerPixel">Bytes per pixel of the texture format</param>
|
||||||
|
/// <param name="isLinear">True if the texture is linear, false for block linear</param>
|
||||||
|
/// <returns>The minimum width of the texture with the same amount of GOBs per row</returns>
|
||||||
|
private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPerPixel, bool isLinear)
|
||||||
|
{
|
||||||
|
if (isLinear || (uint)minimumWidth >= (uint)width)
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the minimum possible that would not cause data loss
|
||||||
|
// and would be still within the same GOB (aligned size would be the same).
|
||||||
|
// This is useful for render and copy operations, where we don't know the
|
||||||
|
// exact width of the texture, but it doesn't matter, as long the texture is
|
||||||
|
// at least as large as the region being rendered or copied.
|
||||||
|
|
||||||
|
int alignment = 64 / bytesPerPixel;
|
||||||
|
int widthAligned = BitUtils.AlignUp(width, alignment);
|
||||||
|
|
||||||
|
return Math.Clamp(widthAligned - alignment + 1, minimumWidth, widthAligned);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -423,7 +451,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
||||||
/// <param name="info">Texture information of the texture to be found or created</param>
|
/// <param name="info">Texture information of the texture to be found or created</param>
|
||||||
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
|
||||||
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(
|
public Texture FindOrCreateTexture(
|
||||||
@@ -431,7 +458,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureSearchFlags flags,
|
TextureSearchFlags flags,
|
||||||
TextureInfo info,
|
TextureInfo info,
|
||||||
int layerSize = 0,
|
int layerSize = 0,
|
||||||
Size? sizeHint = null,
|
|
||||||
MultiRange? range = null)
|
MultiRange? range = null)
|
||||||
{
|
{
|
||||||
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
||||||
@@ -448,6 +474,29 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
address = memoryManager.Translate(info.GpuAddress);
|
address = memoryManager.Translate(info.GpuAddress);
|
||||||
|
|
||||||
|
// If the start address is unmapped, let's try to find a page of memory that is mapped.
|
||||||
|
if (address == MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
// Make sure that the dimensions are valid before calculating the texture size.
|
||||||
|
if (info.Width < 1 || info.Height < 1 || info.Levels < 1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((info.Target == Target.Texture3D ||
|
||||||
|
info.Target == Target.Texture2DArray ||
|
||||||
|
info.Target == Target.Texture2DMultisampleArray ||
|
||||||
|
info.Target == Target.CubemapArray) && info.DepthOrLayers < 1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong dataSize = (ulong)info.CalculateSizeInfo(layerSize).TotalSize;
|
||||||
|
|
||||||
|
address = memoryManager.TranslateFirstMapped(info.GpuAddress, dataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If address is still invalid, the texture is fully unmapped, so it has no data, just return null.
|
||||||
if (address == MemoryManager.PteUnmapped)
|
if (address == MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -512,8 +561,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
|
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
@@ -568,6 +615,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
|
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
|
||||||
info,
|
info,
|
||||||
range.Value,
|
range.Value,
|
||||||
|
isSamplerTexture,
|
||||||
sizeInfo.LayerSize,
|
sizeInfo.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
@@ -598,17 +646,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (oInfo.Compatibility == TextureViewCompatibility.Full)
|
if (oInfo.Compatibility == TextureViewCompatibility.Full)
|
||||||
{
|
{
|
||||||
TextureInfo adjInfo = AdjustSizes(overlap, info, oInfo.FirstLevel);
|
|
||||||
|
|
||||||
if (!isSamplerTexture)
|
if (!isSamplerTexture)
|
||||||
{
|
{
|
||||||
info = adjInfo;
|
// If this is not a sampler texture, the size might be different from the requested size,
|
||||||
|
// so we need to make sure the texture information has the correct size for this base texture,
|
||||||
|
// before creating the view.
|
||||||
|
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
texture = overlap.CreateView(adjInfo, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
|
texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
|
||||||
|
|
||||||
ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
|
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -682,6 +728,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureViewCompatibility compatibility = texture.IsViewCompatible(
|
TextureViewCompatibility compatibility = texture.IsViewCompatible(
|
||||||
overlap.Info,
|
overlap.Info,
|
||||||
overlap.Range,
|
overlap.Range,
|
||||||
|
exactSize: true,
|
||||||
overlap.LayerSize,
|
overlap.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
@@ -792,7 +839,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);
|
// Note: If we allow different sizes for those overlaps,
|
||||||
|
// we need to make sure that the "info" has the correct size for the parent texture here.
|
||||||
|
// Since this is not allowed right now, we don't need to do it.
|
||||||
|
|
||||||
|
TextureInfo overlapInfo = overlap.Info;
|
||||||
|
|
||||||
if (texture.ScaleFactor != overlap.ScaleFactor)
|
if (texture.ScaleFactor != overlap.ScaleFactor)
|
||||||
{
|
{
|
||||||
@@ -856,44 +907,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes a texture's size to match the desired size for samplers,
|
|
||||||
/// or increases a texture's size to fit the region indicated by a size hint.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The desired texture info</param>
|
|
||||||
/// <param name="texture">The texture to resize</param>
|
|
||||||
/// <param name="isSamplerTexture">True if the texture will be used for a sampler, false otherwise</param>
|
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
|
||||||
private void ChangeSizeIfNeeded(TextureInfo info, Texture texture, bool isSamplerTexture, Size? sizeHint)
|
|
||||||
{
|
|
||||||
if (isSamplerTexture)
|
|
||||||
{
|
|
||||||
// If this is used for sampling, the size must match,
|
|
||||||
// otherwise the shader would sample garbage data.
|
|
||||||
// To fix that, we create a new texture with the correct
|
|
||||||
// size, and copy the data from the old one to the new one.
|
|
||||||
|
|
||||||
if (!TextureCompatibility.SizeMatches(texture.Info, info))
|
|
||||||
{
|
|
||||||
texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (sizeHint != null)
|
|
||||||
{
|
|
||||||
// A size hint indicates that data will be used within that range, at least.
|
|
||||||
// If the texture is smaller than the size hint, it must be enlarged to meet it.
|
|
||||||
// The maximum size is provided by the requested info, which generally has an aligned size.
|
|
||||||
|
|
||||||
int width = Math.Max(texture.Info.Width, Math.Min(sizeHint.Value.Width, info.Width));
|
|
||||||
int height = Math.Max(texture.Info.Height, Math.Min(sizeHint.Value.Height, info.Height));
|
|
||||||
|
|
||||||
if (texture.Info.Width != width || texture.Info.Height != height)
|
|
||||||
{
|
|
||||||
texture.ChangeSize(width, height, info.DepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1000,92 +1013,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adjusts the size of the texture information for a given mipmap level,
|
|
||||||
/// based on the size of a parent texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent texture</param>
|
|
||||||
/// <param name="info">The texture information to be adjusted</param>
|
|
||||||
/// <param name="firstLevel">The first level of the texture view</param>
|
|
||||||
/// <returns>The adjusted texture information with the new size</returns>
|
|
||||||
private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
|
|
||||||
{
|
|
||||||
// When the texture is used as view of another texture, we must
|
|
||||||
// ensure that the sizes are valid, otherwise data uploads would fail
|
|
||||||
// (and the size wouldn't match the real size used on the host API).
|
|
||||||
// Given a parent texture from where the view is created, we have the
|
|
||||||
// following rules:
|
|
||||||
// - The view size must be equal to the parent size, divided by (2 ^ l),
|
|
||||||
// where l is the first mipmap level of the view. The division result must
|
|
||||||
// be rounded down, and the result must be clamped to 1.
|
|
||||||
// - If the parent format is compressed, and the view format isn't, the
|
|
||||||
// view size is calculated as above, but the width and height of the
|
|
||||||
// view must be also divided by the compressed format block width and height.
|
|
||||||
// - If the parent format is not compressed, and the view is, the view
|
|
||||||
// size is calculated as described on the first point, but the width and height
|
|
||||||
// of the view must be also multiplied by the block width and height.
|
|
||||||
int width = Math.Max(1, parent.Info.Width >> firstLevel);
|
|
||||||
int height = Math.Max(1, parent.Info.Height >> firstLevel);
|
|
||||||
|
|
||||||
if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
|
|
||||||
{
|
|
||||||
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
|
|
||||||
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
|
|
||||||
}
|
|
||||||
else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
|
|
||||||
{
|
|
||||||
width *= info.FormatInfo.BlockWidth;
|
|
||||||
height *= info.FormatInfo.BlockHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
int depthOrLayers;
|
|
||||||
|
|
||||||
if (info.Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
depthOrLayers = info.DepthOrLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2D and 2D multisample textures are not considered compatible.
|
|
||||||
// This specific case is required for copies, where the source texture might be multisample.
|
|
||||||
// In this case, we inherit the parent texture multisample state.
|
|
||||||
Target target = info.Target;
|
|
||||||
int samplesInX = info.SamplesInX;
|
|
||||||
int samplesInY = info.SamplesInY;
|
|
||||||
|
|
||||||
if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
|
|
||||||
{
|
|
||||||
target = Target.Texture2DMultisample;
|
|
||||||
samplesInX = parent.Info.SamplesInX;
|
|
||||||
samplesInY = parent.Info.SamplesInY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TextureInfo(
|
|
||||||
info.GpuAddress,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depthOrLayers,
|
|
||||||
info.Levels,
|
|
||||||
samplesInX,
|
|
||||||
samplesInY,
|
|
||||||
info.Stride,
|
|
||||||
info.IsLinear,
|
|
||||||
info.GobBlocksInY,
|
|
||||||
info.GobBlocksInZ,
|
|
||||||
info.GobBlocksInTileX,
|
|
||||||
target,
|
|
||||||
info.FormatInfo,
|
|
||||||
info.DepthStencilMode,
|
|
||||||
info.SwizzleR,
|
|
||||||
info.SwizzleG,
|
|
||||||
info.SwizzleB,
|
|
||||||
info.SwizzleA);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a texture creation information from texture information.
|
/// Gets a texture creation information from texture information.
|
||||||
/// This can be used to create new host textures.
|
/// This can be used to create new host textures.
|
||||||
@@ -1175,19 +1102,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues the removal of a texture from the auto delete cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This function is thread safe and can be called from any thread.
|
|
||||||
/// The texture will be deleted on the next time the cache is used.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="texture">The texture to be removed</param>
|
|
||||||
public void QueueAutoDeleteCacheRemoval(Texture texture)
|
|
||||||
{
|
|
||||||
_cache.RemoveDeferred(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -380,42 +380,37 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lhs">Texture information of the texture view</param>
|
/// <param name="lhs">Texture information of the texture view</param>
|
||||||
/// <param name="rhs">Texture information of the texture view to match against</param>
|
/// <param name="rhs">Texture information of the texture view to match against</param>
|
||||||
|
/// <param name="exact">Indicates if the sizes must be exactly equal</param>
|
||||||
/// <param name="level">Mipmap level of the texture view in relation to this texture</param>
|
/// <param name="level">Mipmap level of the texture view in relation to this texture</param>
|
||||||
/// <returns>The view compatibility level of the view sizes</returns>
|
/// <returns>The view compatibility level of the view sizes</returns>
|
||||||
public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
|
public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact, int level)
|
||||||
{
|
{
|
||||||
Size size = GetAlignedSize(lhs, level);
|
Size lhsAlignedSize = GetAlignedSize(lhs, level);
|
||||||
|
Size rhsAlignedSize = GetAlignedSize(rhs);
|
||||||
|
|
||||||
Size otherSize = GetAlignedSize(rhs);
|
Size lhsSize = GetSizeInBlocks(lhs, level);
|
||||||
|
Size rhsSize = GetSizeInBlocks(rhs);
|
||||||
|
|
||||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
|
|
||||||
// For copies, we can copy a subset of the 3D texture slices,
|
// For copies, we can copy a subset of the 3D texture slices,
|
||||||
// so the depth may be different in this case.
|
// so the depth may be different in this case.
|
||||||
if (rhs.Target == Target.Texture3D && size.Depth != otherSize.Depth)
|
if (rhs.Target == Target.Texture3D && lhsSize.Depth != rhsSize.Depth)
|
||||||
{
|
{
|
||||||
result = TextureViewCompatibility.CopyOnly;
|
result = TextureViewCompatibility.CopyOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size.Width == otherSize.Width && size.Height == otherSize.Height)
|
// Some APIs align the width for copy and render target textures,
|
||||||
|
// so the width may not match in this case for different uses of the same texture.
|
||||||
|
// To account for this, we compare the aligned width here.
|
||||||
|
// We expect height to always match exactly, if the texture is the same.
|
||||||
|
if (lhsAlignedSize.Width == rhsAlignedSize.Width && lhsSize.Height == rhsSize.Height)
|
||||||
{
|
{
|
||||||
if (level > 0 && result == TextureViewCompatibility.Full)
|
return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
|
||||||
{
|
? TextureViewCompatibility.CopyOnly
|
||||||
// A resize should not change the aligned size of the largest mip.
|
: result;
|
||||||
// If it would, then create a copy dependency rather than a full view.
|
|
||||||
|
|
||||||
Size mip0SizeLhs = GetAlignedSize(lhs);
|
|
||||||
Size mip0SizeRhs = GetLargestAlignedSize(rhs, level);
|
|
||||||
|
|
||||||
if (mip0SizeLhs.Width != mip0SizeRhs.Width || mip0SizeLhs.Height != mip0SizeRhs.Height)
|
|
||||||
{
|
|
||||||
result = TextureViewCompatibility.CopyOnly;
|
|
||||||
}
|
}
|
||||||
}
|
else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else if (lhs.IsLinear && rhs.IsLinear)
|
|
||||||
{
|
{
|
||||||
// Copy between linear textures with matching stride.
|
// Copy between linear textures with matching stride.
|
||||||
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
|
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
|
||||||
@@ -454,57 +449,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
/// <param name="lhs">Texture information to compare</param>
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
/// <param name="rhs">Texture information to compare with</param>
|
||||||
/// <returns>True if the size matches, false otherwise</returns>
|
/// <param name="exact">Indicates if the size must be exactly equal between the textures, or if <paramref name="rhs"/> is allowed to be larger</param>
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs)
|
|
||||||
{
|
|
||||||
return SizeMatches(lhs, rhs, alignSizes: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture sizes of the supplied texture informations match the given level
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
|
||||||
/// <param name="level">Mipmap level of this texture to compare with</param>
|
|
||||||
/// <returns>True if the size matches with the level, false otherwise</returns>
|
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
|
|
||||||
{
|
|
||||||
return Math.Max(1, lhs.Width >> level) == rhs.Width &&
|
|
||||||
Math.Max(1, lhs.Height >> level) == rhs.Height &&
|
|
||||||
Math.Max(1, lhs.GetDepth() >> level) == rhs.GetDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture sizes of the supplied texture informations match.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
|
||||||
/// <param name="alignSizes">True to align the sizes according to the texture layout for comparison</param>
|
|
||||||
/// <param name="lhsLevel">Mip level of the lhs texture. Aligned sizes are compared for the largest mip</param>
|
|
||||||
/// <returns>True if the sizes matches, false otherwise</returns>
|
/// <returns>True if the sizes matches, false otherwise</returns>
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes, int lhsLevel = 0)
|
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact)
|
||||||
{
|
{
|
||||||
if (lhs.GetLayers() != rhs.GetLayers())
|
if (lhs.GetLayers() != rhs.GetLayers())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTextureBuffer = lhs.Target == Target.TextureBuffer || rhs.Target == Target.TextureBuffer;
|
Size lhsSize = GetSizeInBlocks(lhs);
|
||||||
|
Size rhsSize = GetSizeInBlocks(rhs);
|
||||||
|
|
||||||
if (alignSizes && !isTextureBuffer)
|
if (exact || lhs.IsLinear || rhs.IsLinear)
|
||||||
{
|
{
|
||||||
Size size0 = GetLargestAlignedSize(lhs, lhsLevel);
|
return lhsSize.Width == rhsSize.Width &&
|
||||||
Size size1 = GetLargestAlignedSize(rhs, lhsLevel);
|
lhsSize.Height == rhsSize.Height &&
|
||||||
|
lhsSize.Depth == rhsSize.Depth;
|
||||||
return size0.Width == size1.Width &&
|
|
||||||
size0.Height == size1.Height &&
|
|
||||||
size0.Depth == size1.Depth;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return lhs.Width == rhs.Width &&
|
Size lhsAlignedSize = GetAlignedSize(lhs);
|
||||||
lhs.Height == rhs.Height &&
|
Size rhsAlignedSize = GetAlignedSize(rhs);
|
||||||
lhs.GetDepth() == rhs.GetDepth();
|
|
||||||
|
return lhsAlignedSize.Width == rhsAlignedSize.Width &&
|
||||||
|
lhsSize.Width >= rhsSize.Width &&
|
||||||
|
lhsSize.Height == rhsSize.Height &&
|
||||||
|
lhsSize.Depth == rhsSize.Depth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,22 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the aligned sizes of the specified texture information, shifted to the largest mip from a given level.
|
|
||||||
/// The alignment depends on the texture layout and format bytes per pixel.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Texture information to calculate the aligned size from</param>
|
|
||||||
/// <param name="level">Mipmap level for texture views. Shifts the aligned size to represent the largest mip level</param>
|
|
||||||
/// <returns>The aligned texture size of the largest mip level</returns>
|
|
||||||
public static Size GetLargestAlignedSize(TextureInfo info, int level)
|
|
||||||
{
|
|
||||||
int width = info.Width << level;
|
|
||||||
int height = info.Height << level;
|
|
||||||
int depth = info.GetDepth() << level;
|
|
||||||
|
|
||||||
return GetAlignedSize(info, width, height, depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the aligned sizes of the specified texture information.
|
/// Gets the aligned sizes of the specified texture information.
|
||||||
/// The alignment depends on the texture layout and format bytes per pixel.
|
/// The alignment depends on the texture layout and format bytes per pixel.
|
||||||
@@ -575,6 +530,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return GetAlignedSize(info, width, height, depth);
|
return GetAlignedSize(info, width, height, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size in blocks for the given texture information.
|
||||||
|
/// For non-compressed formats, that's the same as the regular size.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">Texture information to calculate the aligned size from</param>
|
||||||
|
/// <param name="level">Mipmap level for texture views</param>
|
||||||
|
/// <returns>The texture size in blocks</returns>
|
||||||
|
public static Size GetSizeInBlocks(TextureInfo info, int level = 0)
|
||||||
|
{
|
||||||
|
int width = Math.Max(1, info.Width >> level);
|
||||||
|
int height = Math.Max(1, info.Height >> level);
|
||||||
|
int depth = Math.Max(1, info.GetDepth() >> level);
|
||||||
|
|
||||||
|
return new Size(
|
||||||
|
BitUtils.DivRoundUp(width, info.FormatInfo.BlockWidth),
|
||||||
|
BitUtils.DivRoundUp(height, info.FormatInfo.BlockHeight),
|
||||||
|
depth);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if it's possible to create a view with the layout of the second texture information from the first.
|
/// Check if it's possible to create a view with the layout of the second texture information from the first.
|
||||||
/// The layout information is composed of the Stride for linear textures, or GOB block size
|
/// The layout information is composed of the Stride for linear textures, or GOB block size
|
||||||
|
@@ -336,24 +336,23 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (_loadNeeded[baseHandle + i])
|
if (_loadNeeded[baseHandle + i])
|
||||||
{
|
{
|
||||||
var info = GetHandleInformation(baseHandle + i);
|
var info = GetHandleInformation(baseHandle + i);
|
||||||
int offsetIndex = info.Index;
|
|
||||||
|
|
||||||
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
|
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
|
||||||
for (int layer = 0; layer < info.Layers; layer++)
|
for (int layer = 0; layer < info.Layers; layer++)
|
||||||
{
|
{
|
||||||
for (int level = 0; level < info.Levels; level++)
|
for (int level = 0; level < info.Levels; level++)
|
||||||
{
|
{
|
||||||
|
int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
|
||||||
|
|
||||||
int offset = _allOffsets[offsetIndex];
|
int offset = _allOffsets[offsetIndex];
|
||||||
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
|
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
|
||||||
int size = endOffset - offset;
|
int size = endOffset - offset;
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
||||||
|
|
||||||
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
|
||||||
|
|
||||||
Storage.SetData(result, info.BaseLayer, info.BaseLevel);
|
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
|
||||||
|
|
||||||
offsetIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -434,32 +433,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a texture was modified by the GPU.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">The texture to be checked</param>
|
|
||||||
/// <returns>True if any region of the texture was modified by the GPU, false otherwise</returns>
|
|
||||||
public bool AnyModified(Texture texture)
|
|
||||||
{
|
|
||||||
bool anyModified = false;
|
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < regionCount; i++)
|
|
||||||
{
|
|
||||||
TextureGroupHandle group = _handles[baseHandle + i];
|
|
||||||
|
|
||||||
if (group.Modified)
|
|
||||||
{
|
|
||||||
anyModified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return anyModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flush modified ranges for a given texture.
|
/// Flush modified ranges for a given texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -881,7 +854,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>A CpuRegionHandle covering the given range</returns>
|
/// <returns>A CpuRegionHandle covering the given range</returns>
|
||||||
private CpuRegionHandle GenerateHandle(ulong address, ulong size)
|
private CpuRegionHandle GenerateHandle(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return _physicalMemory.BeginTracking(address, size);
|
return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@@ -292,5 +294,88 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
layerSize);
|
layerSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates texture information for a given mipmap level of the specified parent texture and this information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent texture</param>
|
||||||
|
/// <param name="firstLevel">The first level of the texture view</param>
|
||||||
|
/// <returns>The adjusted texture information with the new size</returns>
|
||||||
|
public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel)
|
||||||
|
{
|
||||||
|
// When the texture is used as view of another texture, we must
|
||||||
|
// ensure that the sizes are valid, otherwise data uploads would fail
|
||||||
|
// (and the size wouldn't match the real size used on the host API).
|
||||||
|
// Given a parent texture from where the view is created, we have the
|
||||||
|
// following rules:
|
||||||
|
// - The view size must be equal to the parent size, divided by (2 ^ l),
|
||||||
|
// where l is the first mipmap level of the view. The division result must
|
||||||
|
// be rounded down, and the result must be clamped to 1.
|
||||||
|
// - If the parent format is compressed, and the view format isn't, the
|
||||||
|
// view size is calculated as above, but the width and height of the
|
||||||
|
// view must be also divided by the compressed format block width and height.
|
||||||
|
// - If the parent format is not compressed, and the view is, the view
|
||||||
|
// size is calculated as described on the first point, but the width and height
|
||||||
|
// of the view must be also multiplied by the block width and height.
|
||||||
|
int width = Math.Max(1, parent.Info.Width >> firstLevel);
|
||||||
|
int height = Math.Max(1, parent.Info.Height >> firstLevel);
|
||||||
|
|
||||||
|
if (parent.Info.FormatInfo.IsCompressed && !FormatInfo.IsCompressed)
|
||||||
|
{
|
||||||
|
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
|
||||||
|
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
|
||||||
|
}
|
||||||
|
else if (!parent.Info.FormatInfo.IsCompressed && FormatInfo.IsCompressed)
|
||||||
|
{
|
||||||
|
width *= FormatInfo.BlockWidth;
|
||||||
|
height *= FormatInfo.BlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int depthOrLayers;
|
||||||
|
|
||||||
|
if (Target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
depthOrLayers = DepthOrLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2D and 2D multisample textures are not considered compatible.
|
||||||
|
// This specific case is required for copies, where the source texture might be multisample.
|
||||||
|
// In this case, we inherit the parent texture multisample state.
|
||||||
|
Target target = Target;
|
||||||
|
int samplesInX = SamplesInX;
|
||||||
|
int samplesInY = SamplesInY;
|
||||||
|
|
||||||
|
if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
|
||||||
|
{
|
||||||
|
target = Target.Texture2DMultisample;
|
||||||
|
samplesInX = parent.Info.SamplesInX;
|
||||||
|
samplesInY = parent.Info.SamplesInY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureInfo(
|
||||||
|
GpuAddress,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depthOrLayers,
|
||||||
|
Levels,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
Stride,
|
||||||
|
IsLinear,
|
||||||
|
GobBlocksInY,
|
||||||
|
GobBlocksInZ,
|
||||||
|
GobBlocksInTileX,
|
||||||
|
target,
|
||||||
|
FormatInfo,
|
||||||
|
DepthStencilMode,
|
||||||
|
SwizzleR,
|
||||||
|
SwizzleG,
|
||||||
|
SwizzleB,
|
||||||
|
SwizzleA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -437,22 +437,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// All attachments other than <paramref name="index"/> will be unbound.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="index">Index of the render target color to be updated</param>
|
|
||||||
public void UpdateRenderTarget(int index)
|
|
||||||
{
|
|
||||||
new Span<ITexture>(_rtHostColors).Fill(null);
|
|
||||||
_rtHostColors[index] = _rtColors[index]?.HostTexture;
|
|
||||||
_rtHostDs = null;
|
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
/// Update host framebuffer attachments based on currently bound render target buffers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -77,22 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (texture.ChangedSize)
|
// On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation.
|
||||||
{
|
|
||||||
// Texture changed size at one point - it may be a different size than the sampler expects.
|
|
||||||
// This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before.
|
|
||||||
|
|
||||||
int baseLevel = descriptor.UnpackBaseLevel();
|
|
||||||
int width = Math.Max(1, descriptor.UnpackWidth() >> baseLevel);
|
|
||||||
int height = Math.Max(1, descriptor.UnpackHeight() >> baseLevel);
|
|
||||||
|
|
||||||
if (texture.Info.Width != width || texture.Info.Height != height)
|
|
||||||
{
|
|
||||||
texture.ChangeSize(width, height, texture.Info.DepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory is automatically synchronized on texture creation.
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
enum TextureSearchFlags
|
enum TextureSearchFlags
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Strict = 1 << 0,
|
|
||||||
ForSampler = 1 << 1,
|
ForSampler = 1 << 1,
|
||||||
ForCopy = 1 << 2,
|
ForCopy = 1 << 2,
|
||||||
WithUpscale = 1 << 3,
|
WithUpscale = 1 << 3,
|
||||||
|
@@ -105,13 +105,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (_useGranular)
|
if (_useGranular)
|
||||||
{
|
{
|
||||||
_memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles);
|
_memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Buffer, baseHandles);
|
||||||
|
|
||||||
_memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction);
|
_memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_memoryTracking = physicalMemory.BeginTracking(address, size);
|
_memoryTracking = physicalMemory.BeginTracking(address, size, ResourceKind.Buffer);
|
||||||
|
|
||||||
if (baseHandles != null)
|
if (baseHandles != null)
|
||||||
{
|
{
|
||||||
|
@@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
|
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
|
||||||
|
|
||||||
buffer.SignalModified(address, size);
|
memoryManager.Physical.FillTrackedResource(address, size, value, ResourceKind.Buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -583,6 +583,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return UnpackPaFromPte(pte) + (va & PageMask);
|
return UnpackPaFromPte(pte) + (va & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Translates a GPU virtual address to a CPU virtual address on the first mapped page of memory
|
||||||
|
/// on the specified region.
|
||||||
|
/// If no page is mapped on the specified region, <see cref="PteUnmapped"/> is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to be translated</param>
|
||||||
|
/// <param name="size">Size of the range to be translated</param>
|
||||||
|
/// <returns>CPU virtual address, or <see cref="PteUnmapped"/> if unmapped</returns>
|
||||||
|
public ulong TranslateFirstMapped(ulong va, ulong size)
|
||||||
|
{
|
||||||
|
if (!ValidateAddress(va))
|
||||||
|
{
|
||||||
|
return PteUnmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong endVa = va + size;
|
||||||
|
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
for (; va < endVa && pte == PteUnmapped; va += PageSize - (va & PageMask))
|
||||||
|
{
|
||||||
|
pte = GetPte(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return PteUnmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnpackPaFromPte(pte) + (va & PageMask);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the kind of a given memory page.
|
/// Gets the kind of a given memory page.
|
||||||
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
||||||
|
@@ -7,6 +7,7 @@ using Ryujinx.Memory.Range;
|
|||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
@@ -295,23 +296,41 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fills the specified memory region with a 32-bit integer value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="value">Value to fill the region with</param>
|
||||||
|
/// <param name="kind">Kind of the resource being filled, which will not be signalled as CPU modified</param>
|
||||||
|
public void FillTrackedResource(ulong address, ulong size, uint value, ResourceKind kind)
|
||||||
|
{
|
||||||
|
_cpuMemory.SignalMemoryTracking(address, size, write: true, precise: true, (int)kind);
|
||||||
|
|
||||||
|
using WritableRegion region = _cpuMemory.GetWritableRegion(address, (int)size);
|
||||||
|
|
||||||
|
MemoryMarshal.Cast<byte, uint>(region.Memory.Span).Fill(value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
|
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="kind">Kind of the resource being tracked</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public CpuRegionHandle BeginTracking(ulong address, ulong size)
|
public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind)
|
||||||
{
|
{
|
||||||
return _cpuMemory.BeginTracking(address, size);
|
return _cpuMemory.BeginTracking(address, size, (int)kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
|
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="range">Ranges of physical memory where the data is located</param>
|
/// <param name="range">Ranges of physical memory where the data is located</param>
|
||||||
|
/// <param name="kind">Kind of the resource being tracked</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public GpuRegionHandle BeginTracking(MultiRange range)
|
public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind)
|
||||||
{
|
{
|
||||||
var cpuRegionHandles = new CpuRegionHandle[range.Count];
|
var cpuRegionHandles = new CpuRegionHandle[range.Count];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -321,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
var currentRange = range.GetSubRange(i);
|
var currentRange = range.GetSubRange(i);
|
||||||
if (currentRange.Address != MemoryManager.PteUnmapped)
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size);
|
cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size, (int)kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,12 +357,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="kind">Kind of the resource being tracked</param>
|
||||||
/// <param name="handles">Handles to inherit state from or reuse</param>
|
/// <param name="handles">Handles to inherit state from or reuse</param>
|
||||||
/// <param name="granularity">Desired granularity of write tracking</param>
|
/// <param name="granularity">Desired granularity of write tracking</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
|
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
|
||||||
{
|
{
|
||||||
return _cpuMemory.BeginGranularTracking(address, size, handles, granularity);
|
return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -351,11 +371,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="kind">Kind of the resource being tracked</param>
|
||||||
/// <param name="granularity">Desired granularity of write tracking</param>
|
/// <param name="granularity">Desired granularity of write tracking</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity = 4096)
|
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096)
|
||||||
{
|
{
|
||||||
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
|
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
13
Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs
Normal file
13
Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Kind of a GPU resource.
|
||||||
|
/// </summary>
|
||||||
|
enum ResourceKind
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Buffer,
|
||||||
|
Texture,
|
||||||
|
Pool
|
||||||
|
}
|
||||||
|
}
|
@@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
pt.AcquireCallback(_context, pt.UserObj);
|
pt.AcquireCallback(_context, pt.UserObj);
|
||||||
|
|
||||||
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
|
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range);
|
||||||
|
|
||||||
pt.Cache.Tick();
|
pt.Cache.Tick();
|
||||||
|
|
||||||
|
@@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
class Program : IProgram
|
class Program : IProgram
|
||||||
{
|
{
|
||||||
|
private const int MaxShaderLogLength = 2048;
|
||||||
|
|
||||||
public int Handle { get; private set; }
|
public int Handle { get; private set; }
|
||||||
|
|
||||||
public bool IsLinked
|
public bool IsLinked
|
||||||
@@ -115,9 +117,16 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
{
|
{
|
||||||
// Use GL.GetProgramInfoLog(Handle), it may be too long to print on the log.
|
|
||||||
_status = ProgramLinkStatus.Failure;
|
_status = ProgramLinkStatus.Failure;
|
||||||
Logger.Debug?.Print(LogClass.Gpu, "Shader linking failed.");
|
|
||||||
|
string log = GL.GetProgramInfoLog(Handle);
|
||||||
|
|
||||||
|
if (log.Length > MaxShaderLogLength)
|
||||||
|
{
|
||||||
|
log = log.Substring(0, MaxShaderLogLength) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{log}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -397,20 +397,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
||||||
{
|
{
|
||||||
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
|
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
|
||||||
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
|
|
||||||
|
|
||||||
foreach (int attr in inputs)
|
if (iaIndexing && !perPatch)
|
||||||
{
|
|
||||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
|
|
||||||
if (iaIndexing && isUserAttr && !perPatch)
|
|
||||||
{
|
|
||||||
if (context.InputsArray == null)
|
|
||||||
{
|
{
|
||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
||||||
@@ -433,9 +421,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.AddGlobalVariable(spvVar);
|
context.AddGlobalVariable(spvVar);
|
||||||
context.InputsArray = spvVar;
|
context.InputsArray = spvVar;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
|
||||||
|
|
||||||
|
foreach (int attr in inputs)
|
||||||
{
|
{
|
||||||
|
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
||||||
|
|
||||||
|
if (iaIndexing && isUserAttr && !perPatch)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PixelImap iq = PixelImap.Unused;
|
PixelImap iq = PixelImap.Unused;
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
if (context.Config.Stage == ShaderStage.Fragment)
|
||||||
@@ -459,25 +461,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
|
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
||||||
{
|
{
|
||||||
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
|
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
|
||||||
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
|
|
||||||
|
|
||||||
foreach (int attr in outputs)
|
if (oaIndexing && !perPatch)
|
||||||
{
|
|
||||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
|
|
||||||
if (oaIndexing && isUserAttr && !perPatch)
|
|
||||||
{
|
|
||||||
if (context.OutputsArray == null)
|
|
||||||
{
|
{
|
||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
||||||
@@ -495,11 +484,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.AddGlobalVariable(spvVar);
|
context.AddGlobalVariable(spvVar);
|
||||||
context.OutputsArray = spvVar;
|
context.OutputsArray = spvVar;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
|
||||||
|
|
||||||
|
foreach (int attr in outputs)
|
||||||
{
|
{
|
||||||
DeclareOutputAttribute(context, attr, perPatch);
|
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
||||||
|
|
||||||
|
if (oaIndexing && isUserAttr && !perPatch)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclareOutputAttribute(context, attr, perPatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
if (context.Config.Stage == ShaderStage.Vertex)
|
||||||
|
@@ -10,11 +10,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000;
|
private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000;
|
||||||
|
|
||||||
private const int MinDrawCountForFlush = 10;
|
private const int MinDrawCountForFlush = 10;
|
||||||
|
private const int MinConsecutiveQueryForFlush = 10;
|
||||||
private const int InitialQueryCountForFlush = 32;
|
private const int InitialQueryCountForFlush = 32;
|
||||||
|
|
||||||
private long _lastFlush;
|
private long _lastFlush;
|
||||||
private ulong _lastDrawCount;
|
private ulong _lastDrawCount;
|
||||||
private bool _hasPendingQuery;
|
private bool _hasPendingQuery;
|
||||||
|
private int _consecutiveQueries;
|
||||||
private int _queryCount;
|
private int _queryCount;
|
||||||
|
|
||||||
private int[] _queryCountHistory = new int[3];
|
private int[] _queryCountHistory = new int[3];
|
||||||
@@ -27,11 +29,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_lastDrawCount = drawCount;
|
_lastDrawCount = drawCount;
|
||||||
|
|
||||||
_hasPendingQuery = false;
|
_hasPendingQuery = false;
|
||||||
|
_consecutiveQueries = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RegisterPendingQuery()
|
public bool RegisterPendingQuery()
|
||||||
{
|
{
|
||||||
_hasPendingQuery = true;
|
_hasPendingQuery = true;
|
||||||
|
_consecutiveQueries++;
|
||||||
_remainingQueries--;
|
_remainingQueries--;
|
||||||
|
|
||||||
_queryCountHistory[_queryCountHistoryIndex]++;
|
_queryCountHistory[_queryCountHistoryIndex]++;
|
||||||
@@ -65,15 +69,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _hasPendingQuery;
|
return _hasPendingQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShouldFlush(ulong drawCount)
|
public bool ShouldFlushAttachmentChange(ulong drawCount)
|
||||||
{
|
{
|
||||||
_queryCount = 0;
|
_queryCount = 0;
|
||||||
|
|
||||||
if (_hasPendingQuery)
|
// Flush when there's an attachment change out of a large block of queries.
|
||||||
|
if (_consecutiveQueries > MinConsecutiveQueryForFlush)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_consecutiveQueries = 0;
|
||||||
|
|
||||||
long draws = (long)(drawCount - _lastDrawCount);
|
long draws = (long)(drawCount - _lastDrawCount);
|
||||||
|
|
||||||
if (draws < MinDrawCountForFlush)
|
if (draws < MinDrawCountForFlush)
|
||||||
|
@@ -39,7 +39,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
BufferUsageFlags.VertexBufferBit |
|
BufferUsageFlags.VertexBufferBit |
|
||||||
BufferUsageFlags.TransformFeedbackBufferBitExt;
|
BufferUsageFlags.TransformFeedbackBufferBitExt;
|
||||||
|
|
||||||
private readonly PhysicalDevice _physicalDevice;
|
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
|
|
||||||
private readonly IdList<BufferHolder> _buffers;
|
private readonly IdList<BufferHolder> _buffers;
|
||||||
@@ -48,9 +47,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public StagingBuffer StagingBuffer { get; }
|
public StagingBuffer StagingBuffer { get; }
|
||||||
|
|
||||||
public BufferManager(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device)
|
public BufferManager(VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
_physicalDevice = physicalDevice;
|
|
||||||
_device = device;
|
_device = device;
|
||||||
_buffers = new IdList<BufferHolder>();
|
_buffers = new IdList<BufferHolder>();
|
||||||
StagingBuffer = new StagingBuffer(gd, this);
|
StagingBuffer = new StagingBuffer(gd, this);
|
||||||
@@ -114,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
allocateFlagsAlt = DefaultBufferMemoryAltFlags;
|
allocateFlagsAlt = DefaultBufferMemoryAltFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt);
|
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
|
||||||
|
|
||||||
if (allocation.Memory.Handle == 0UL)
|
if (allocation.Memory.Handle == 0UL)
|
||||||
{
|
{
|
||||||
|
@@ -8,11 +8,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
VertexBufferAlignment4B = 1,
|
NoTriangleFans = 1,
|
||||||
NoTriangleFans = 1 << 1,
|
NoPointMode = 1 << 1,
|
||||||
NoPointMode = 1 << 2,
|
No3DImageView = 1 << 2,
|
||||||
No3DImageView = 1 << 3,
|
NoLodBias = 1 << 3
|
||||||
NoLodBias = 1 << 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly struct HardwareCapabilities
|
readonly struct HardwareCapabilities
|
||||||
@@ -40,6 +39,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
|
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
|
||||||
public readonly SampleCountFlags SupportedSampleCounts;
|
public readonly SampleCountFlags SupportedSampleCounts;
|
||||||
public readonly PortabilitySubsetFlags PortabilitySubset;
|
public readonly PortabilitySubsetFlags PortabilitySubset;
|
||||||
|
public readonly uint VertexBufferAlignment;
|
||||||
|
|
||||||
public HardwareCapabilities(
|
public HardwareCapabilities(
|
||||||
bool supportsIndexTypeUint8,
|
bool supportsIndexTypeUint8,
|
||||||
@@ -64,7 +64,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
uint maxSubgroupSize,
|
uint maxSubgroupSize,
|
||||||
ShaderStageFlags requiredSubgroupSizeStages,
|
ShaderStageFlags requiredSubgroupSizeStages,
|
||||||
SampleCountFlags supportedSampleCounts,
|
SampleCountFlags supportedSampleCounts,
|
||||||
PortabilitySubsetFlags portabilitySubset)
|
PortabilitySubsetFlags portabilitySubset,
|
||||||
|
uint vertexBufferAlignment)
|
||||||
{
|
{
|
||||||
SupportsIndexTypeUint8 = supportsIndexTypeUint8;
|
SupportsIndexTypeUint8 = supportsIndexTypeUint8;
|
||||||
SupportsCustomBorderColor = supportsCustomBorderColor;
|
SupportsCustomBorderColor = supportsCustomBorderColor;
|
||||||
@@ -89,6 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
|
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
|
||||||
SupportedSampleCounts = supportedSampleCounts;
|
SupportedSampleCounts = supportedSampleCounts;
|
||||||
PortabilitySubset = portabilitySubset;
|
PortabilitySubset = portabilitySubset;
|
||||||
|
VertexBufferAlignment = vertexBufferAlignment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly ISampler _samplerLinear;
|
private readonly ISampler _samplerLinear;
|
||||||
private readonly ISampler _samplerNearest;
|
private readonly ISampler _samplerNearest;
|
||||||
private readonly IProgram _programColorBlit;
|
private readonly IProgram _programColorBlit;
|
||||||
|
private readonly IProgram _programColorBlitMs;
|
||||||
private readonly IProgram _programColorBlitClearAlpha;
|
private readonly IProgram _programColorBlitClearAlpha;
|
||||||
private readonly IProgram _programColorClearF;
|
private readonly IProgram _programColorClearF;
|
||||||
private readonly IProgram _programColorClearSI;
|
private readonly IProgram _programColorClearSI;
|
||||||
@@ -34,7 +35,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly IProgram _programColorCopyToNonMs;
|
private readonly IProgram _programColorCopyToNonMs;
|
||||||
private readonly IProgram _programColorDrawToMs;
|
private readonly IProgram _programColorDrawToMs;
|
||||||
private readonly IProgram _programDepthBlit;
|
private readonly IProgram _programDepthBlit;
|
||||||
|
private readonly IProgram _programDepthBlitMs;
|
||||||
private readonly IProgram _programStencilBlit;
|
private readonly IProgram _programStencilBlit;
|
||||||
|
private readonly IProgram _programStencilBlitMs;
|
||||||
|
|
||||||
public HelperShader(VulkanRenderer gd, Device device)
|
public HelperShader(VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
@@ -62,6 +65,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
|
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
|
});
|
||||||
|
|
||||||
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
@@ -160,6 +169,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
|
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
|
});
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsShaderStencilExport)
|
if (gd.Capabilities.SupportsShaderStencilExport)
|
||||||
{
|
{
|
||||||
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
@@ -167,18 +182,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
|
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Blit(
|
public void Blit(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
TextureView src,
|
TextureView src,
|
||||||
Auto<DisposableImageView> dst,
|
TextureView dst,
|
||||||
int dstWidth,
|
|
||||||
int dstHeight,
|
|
||||||
VkFormat dstFormat,
|
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion,
|
Extents2D dstRegion,
|
||||||
|
int layers,
|
||||||
|
int levels,
|
||||||
bool isDepthOrStencil,
|
bool isDepthOrStencil,
|
||||||
bool linearFilter,
|
bool linearFilter,
|
||||||
bool clearAlpha = false)
|
bool clearAlpha = false)
|
||||||
@@ -187,13 +207,137 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
using var cbs = gd.CommandBufferPool.Rent();
|
using var cbs = gd.CommandBufferPool.Rent();
|
||||||
|
|
||||||
|
var dstFormat = dst.VkFormat;
|
||||||
|
var dstSamples = dst.Info.Samples;
|
||||||
|
|
||||||
|
for (int l = 0; l < levels; l++)
|
||||||
|
{
|
||||||
|
int srcWidth = Math.Max(1, src.Width >> l);
|
||||||
|
int srcHeight = Math.Max(1, src.Height >> l);
|
||||||
|
|
||||||
|
int dstWidth = Math.Max(1, dst.Width >> l);
|
||||||
|
int dstHeight = Math.Max(1, dst.Height >> l);
|
||||||
|
|
||||||
|
var mipSrcRegion = new Extents2D(
|
||||||
|
srcRegion.X1 >> l,
|
||||||
|
srcRegion.Y1 >> l,
|
||||||
|
srcRegion.X2 >> l,
|
||||||
|
srcRegion.Y2 >> l);
|
||||||
|
|
||||||
|
var mipDstRegion = new Extents2D(
|
||||||
|
dstRegion.X1 >> l,
|
||||||
|
dstRegion.Y1 >> l,
|
||||||
|
dstRegion.X2 >> l,
|
||||||
|
dstRegion.Y2 >> l);
|
||||||
|
|
||||||
|
for (int z = 0; z < layers; z++)
|
||||||
|
{
|
||||||
|
var srcView = Create2DLayerView(src, z, l);
|
||||||
|
var dstView = Create2DLayerView(dst, z, l);
|
||||||
|
|
||||||
if (isDepthOrStencil)
|
if (isDepthOrStencil)
|
||||||
{
|
{
|
||||||
BlitDepthStencil(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion);
|
BlitDepthStencil(
|
||||||
|
gd,
|
||||||
|
cbs,
|
||||||
|
srcView,
|
||||||
|
dst.GetImageViewForAttachment(),
|
||||||
|
dstWidth,
|
||||||
|
dstHeight,
|
||||||
|
dstSamples,
|
||||||
|
dstFormat,
|
||||||
|
mipSrcRegion,
|
||||||
|
mipDstRegion);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BlitColor(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
|
BlitColor(
|
||||||
|
gd,
|
||||||
|
cbs,
|
||||||
|
srcView,
|
||||||
|
dst.GetImageViewForAttachment(),
|
||||||
|
dstWidth,
|
||||||
|
dstHeight,
|
||||||
|
dstSamples,
|
||||||
|
dstFormat,
|
||||||
|
false,
|
||||||
|
mipSrcRegion,
|
||||||
|
mipDstRegion,
|
||||||
|
linearFilter,
|
||||||
|
clearAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcView != src)
|
||||||
|
{
|
||||||
|
srcView.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dstView != dst)
|
||||||
|
{
|
||||||
|
dstView.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyColor(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
CommandBufferScoped cbs,
|
||||||
|
TextureView src,
|
||||||
|
TextureView dst,
|
||||||
|
int srcLayer,
|
||||||
|
int dstLayer,
|
||||||
|
int srcLevel,
|
||||||
|
int dstLevel,
|
||||||
|
int depth,
|
||||||
|
int levels)
|
||||||
|
{
|
||||||
|
for (int l = 0; l < levels; l++)
|
||||||
|
{
|
||||||
|
int mipSrcLevel = srcLevel + l;
|
||||||
|
int mipDstLevel = dstLevel + l;
|
||||||
|
|
||||||
|
int srcWidth = Math.Max(1, src.Width >> mipSrcLevel);
|
||||||
|
int srcHeight = Math.Max(1, src.Height >> mipSrcLevel);
|
||||||
|
|
||||||
|
int dstWidth = Math.Max(1, dst.Width >> mipDstLevel);
|
||||||
|
int dstHeight = Math.Max(1, dst.Height >> mipDstLevel);
|
||||||
|
|
||||||
|
var extents = new Extents2D(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Math.Min(srcWidth, dstWidth),
|
||||||
|
Math.Min(srcHeight, dstHeight));
|
||||||
|
|
||||||
|
for (int z = 0; z < depth; z++)
|
||||||
|
{
|
||||||
|
var srcView = Create2DLayerView(src, srcLayer + z, mipSrcLevel);
|
||||||
|
var dstView = Create2DLayerView(dst, dstLayer + z, mipDstLevel);
|
||||||
|
|
||||||
|
BlitColor(
|
||||||
|
gd,
|
||||||
|
cbs,
|
||||||
|
srcView,
|
||||||
|
dstView.GetImageViewForAttachment(),
|
||||||
|
dstView.Width,
|
||||||
|
dstView.Height,
|
||||||
|
dstView.Info.Samples,
|
||||||
|
dstView.VkFormat,
|
||||||
|
dstView.Info.Format.IsDepthOrStencil(),
|
||||||
|
extents,
|
||||||
|
extents,
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (srcView != src)
|
||||||
|
{
|
||||||
|
srcView.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dstView != dst)
|
||||||
|
{
|
||||||
|
dstView.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +348,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Auto<DisposableImageView> dst,
|
Auto<DisposableImageView> dst,
|
||||||
int dstWidth,
|
int dstWidth,
|
||||||
int dstHeight,
|
int dstHeight,
|
||||||
|
int dstSamples,
|
||||||
VkFormat dstFormat,
|
VkFormat dstFormat,
|
||||||
|
bool dstIsDepthOrStencil,
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion,
|
Extents2D dstRegion,
|
||||||
bool linearFilter,
|
bool linearFilter,
|
||||||
@@ -262,8 +408,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
||||||
|
|
||||||
_pipeline.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit);
|
if (dstIsDepthOrStencil)
|
||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
{
|
||||||
|
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
||||||
|
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always));
|
||||||
|
}
|
||||||
|
else if (src.Info.Target.IsMultisample())
|
||||||
|
{
|
||||||
|
_pipeline.SetProgram(_programColorBlitMs);
|
||||||
|
}
|
||||||
|
else if (clearAlpha)
|
||||||
|
{
|
||||||
|
_pipeline.SetProgram(_programColorBlitClearAlpha);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pipeline.SetProgram(_programColorBlit);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat);
|
||||||
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(scissors);
|
||||||
|
|
||||||
@@ -275,6 +438,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_pipeline.SetViewports(viewports, false);
|
_pipeline.SetViewports(viewports, false);
|
||||||
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
|
if (dstIsDepthOrStencil)
|
||||||
|
{
|
||||||
|
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always));
|
||||||
|
}
|
||||||
|
|
||||||
_pipeline.Finish(gd, cbs);
|
_pipeline.Finish(gd, cbs);
|
||||||
|
|
||||||
gd.BufferManager.Delete(bufferHandle);
|
gd.BufferManager.Delete(bufferHandle);
|
||||||
@@ -287,6 +456,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Auto<DisposableImageView> dst,
|
Auto<DisposableImageView> dst,
|
||||||
int dstWidth,
|
int dstWidth,
|
||||||
int dstHeight,
|
int dstHeight,
|
||||||
|
int dstSamples,
|
||||||
VkFormat dstFormat,
|
VkFormat dstFormat,
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion)
|
Extents2D dstRegion)
|
||||||
@@ -339,7 +509,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
||||||
|
|
||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat);
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat);
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(scissors);
|
||||||
_pipeline.SetViewports(viewports, false);
|
_pipeline.SetViewports(viewports, false);
|
||||||
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||||
@@ -406,12 +576,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (isDepth)
|
if (isDepth)
|
||||||
{
|
{
|
||||||
_pipeline.SetProgram(_programDepthBlit);
|
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
||||||
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always));
|
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_pipeline.SetProgram(_programStencilBlit);
|
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit);
|
||||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
|
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,34 +965,26 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
||||||
|
|
||||||
if (src.Info.Target == Target.Texture2DMultisampleArray ||
|
|
||||||
dst.Info.Target == Target.Texture2DMultisampleArray)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < depth; z++)
|
for (int z = 0; z < depth; z++)
|
||||||
{
|
{
|
||||||
var srcView = Create2DLayerView(src, srcLayer + z, format);
|
var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
|
||||||
_pipeline.SetImage(0, dstView, format);
|
_pipeline.SetImage(0, dstView, format);
|
||||||
|
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
if (srcView != src)
|
||||||
|
{
|
||||||
srcView.Release();
|
srcView.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dstView != dst)
|
||||||
|
{
|
||||||
dstView.Release();
|
dstView.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var srcView = Create2DLayerView(src, srcLayer, format);
|
|
||||||
|
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
|
|
||||||
_pipeline.SetImage(0, dst, format);
|
|
||||||
|
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
|
||||||
|
|
||||||
srcView.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
gd.BufferManager.Delete(bufferHandle);
|
gd.BufferManager.Delete(bufferHandle);
|
||||||
|
|
||||||
@@ -906,13 +1068,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var format = GetFormat(src.Info.BytesPerPixel);
|
var format = GetFormat(src.Info.BytesPerPixel);
|
||||||
var vkFormat = FormatTable.GetFormat(format);
|
var vkFormat = FormatTable.GetFormat(format);
|
||||||
|
|
||||||
if (src.Info.Target == Target.Texture2DMultisampleArray ||
|
|
||||||
dst.Info.Target == Target.Texture2DMultisampleArray)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < depth; z++)
|
for (int z = 0; z < depth; z++)
|
||||||
{
|
{
|
||||||
var srcView = Create2DLayerView(src, srcLayer + z, format);
|
var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
|
||||||
var dstView = Create2DLayerView(dst, dstLayer + z);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
|
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
|
||||||
_pipeline.SetRenderTarget(
|
_pipeline.SetRenderTarget(
|
||||||
@@ -925,27 +1084,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
|
||||||
|
if (srcView != src)
|
||||||
|
{
|
||||||
srcView.Release();
|
srcView.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dstView != dst)
|
||||||
|
{
|
||||||
dstView.Release();
|
dstView.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var srcView = Create2DLayerView(src, srcLayer, format);
|
|
||||||
|
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
|
|
||||||
_pipeline.SetRenderTarget(
|
|
||||||
dst.GetView(format).GetImageViewForAttachment(),
|
|
||||||
(uint)dst.Width,
|
|
||||||
(uint)dst.Height,
|
|
||||||
(uint)samples,
|
|
||||||
false,
|
|
||||||
vkFormat);
|
|
||||||
|
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
|
||||||
|
|
||||||
srcView.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
gd.BufferManager.Delete(bufferHandle);
|
gd.BufferManager.Delete(bufferHandle);
|
||||||
|
|
||||||
@@ -1001,14 +1149,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return (samplesInXLog2, samplesInYLog2);
|
return (samplesInXLog2, samplesInYLog2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null)
|
private static TextureView Create2DLayerView(TextureView from, int layer, int level, GAL.Format? format = null)
|
||||||
{
|
{
|
||||||
|
if (from.Info.Target == Target.Texture2D && level == 0 && (format == null || format.Value == from.Info.Format))
|
||||||
|
{
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
var target = from.Info.Target switch
|
var target = from.Info.Target switch
|
||||||
{
|
{
|
||||||
Target.Texture1DArray => Target.Texture1D,
|
Target.Texture1DArray => Target.Texture1D,
|
||||||
Target.Texture2DArray => Target.Texture2D,
|
|
||||||
Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
|
Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
|
||||||
_ => from.Info.Target
|
_ => Target.Texture2D
|
||||||
};
|
};
|
||||||
|
|
||||||
var info = new TextureCreateInfo(
|
var info = new TextureCreateInfo(
|
||||||
@@ -1028,7 +1180,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
from.Info.SwizzleB,
|
from.Info.SwizzleB,
|
||||||
from.Info.SwizzleA);
|
from.Info.SwizzleA);
|
||||||
|
|
||||||
return from.CreateView(info, layer, 0);
|
return from.CreateViewImpl(info, layer, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GAL.Format GetFormat(int bytesPerPixel)
|
private static GAL.Format GetFormat(int bytesPerPixel)
|
||||||
@@ -1177,6 +1329,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_programColorBlitClearAlpha.Dispose();
|
_programColorBlitClearAlpha.Dispose();
|
||||||
_programColorBlit.Dispose();
|
_programColorBlit.Dispose();
|
||||||
|
_programColorBlitMs.Dispose();
|
||||||
_programColorClearF.Dispose();
|
_programColorClearF.Dispose();
|
||||||
_programColorClearSI.Dispose();
|
_programColorClearSI.Dispose();
|
||||||
_programColorClearUI.Dispose();
|
_programColorClearUI.Dispose();
|
||||||
@@ -1186,7 +1339,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_programColorCopyToNonMs.Dispose();
|
_programColorCopyToNonMs.Dispose();
|
||||||
_programColorDrawToMs.Dispose();
|
_programColorDrawToMs.Dispose();
|
||||||
_programDepthBlit.Dispose();
|
_programDepthBlit.Dispose();
|
||||||
|
_programDepthBlitMs.Dispose();
|
||||||
_programStencilBlit?.Dispose();
|
_programStencilBlit?.Dispose();
|
||||||
|
_programStencilBlitMs?.Dispose();
|
||||||
_samplerNearest.Dispose();
|
_samplerNearest.Dispose();
|
||||||
_samplerLinear.Dispose();
|
_samplerLinear.Dispose();
|
||||||
_pipeline.Dispose();
|
_pipeline.Dispose();
|
||||||
|
@@ -9,34 +9,36 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
|
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
private readonly Vk _api;
|
private readonly Vk _api;
|
||||||
|
private readonly PhysicalDevice _physicalDevice;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
||||||
|
private readonly int _blockAlignment;
|
||||||
|
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
|
||||||
|
|
||||||
private int _blockAlignment;
|
public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount)
|
||||||
|
|
||||||
public MemoryAllocator(Vk api, Device device, uint maxMemoryAllocationCount)
|
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
|
_physicalDevice = physicalDevice;
|
||||||
_device = device;
|
_device = device;
|
||||||
_blockLists = new List<MemoryAllocatorBlockList>();
|
_blockLists = new List<MemoryAllocatorBlockList>();
|
||||||
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
|
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
|
||||||
|
|
||||||
|
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
PhysicalDevice physicalDevice,
|
|
||||||
MemoryRequirements requirements,
|
MemoryRequirements requirements,
|
||||||
MemoryPropertyFlags flags = 0)
|
MemoryPropertyFlags flags = 0)
|
||||||
{
|
{
|
||||||
return AllocateDeviceMemory(physicalDevice, requirements, flags, flags);
|
return AllocateDeviceMemory(requirements, flags, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
PhysicalDevice physicalDevice,
|
|
||||||
MemoryRequirements requirements,
|
MemoryRequirements requirements,
|
||||||
MemoryPropertyFlags flags,
|
MemoryPropertyFlags flags,
|
||||||
MemoryPropertyFlags alternativeFlags)
|
MemoryPropertyFlags alternativeFlags)
|
||||||
{
|
{
|
||||||
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags);
|
int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
|
||||||
if (memoryTypeIndex < 0)
|
if (memoryTypeIndex < 0)
|
||||||
{
|
{
|
||||||
return default;
|
return default;
|
||||||
@@ -65,20 +67,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return newBl.Allocate(size, alignment, map);
|
return newBl.Allocate(size, alignment, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int FindSuitableMemoryTypeIndex(
|
private int FindSuitableMemoryTypeIndex(
|
||||||
Vk api,
|
|
||||||
PhysicalDevice physicalDevice,
|
|
||||||
uint memoryTypeBits,
|
uint memoryTypeBits,
|
||||||
MemoryPropertyFlags flags,
|
MemoryPropertyFlags flags,
|
||||||
MemoryPropertyFlags alternativeFlags)
|
MemoryPropertyFlags alternativeFlags)
|
||||||
{
|
{
|
||||||
int bestCandidateIndex = -1;
|
int bestCandidateIndex = -1;
|
||||||
|
|
||||||
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
|
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
|
||||||
|
|
||||||
for (int i = 0; i < properties.MemoryTypeCount; i++)
|
|
||||||
{
|
{
|
||||||
var type = properties.MemoryTypes[i];
|
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
|
||||||
|
|
||||||
if ((memoryTypeBits & (1 << i)) != 0)
|
if ((memoryTypeBits & (1 << i)) != 0)
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
@@ -650,9 +651,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_newState.DepthWriteEnable = oldDepthWriteEnable;
|
_newState.DepthWriteEnable = oldDepthWriteEnable;
|
||||||
_newState.Topology = oldTopology;
|
_newState.Topology = oldTopology;
|
||||||
|
|
||||||
DynamicState.Viewports = oldViewports;
|
DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
|
||||||
DynamicState.ViewportsCount = (int)oldViewportsCount;
|
|
||||||
DynamicState.SetViewportsDirty();
|
|
||||||
|
|
||||||
_newState.ViewportsCount = oldViewportsCount;
|
_newState.ViewportsCount = oldViewportsCount;
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
@@ -1138,7 +1137,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
buffer.Dispose();
|
buffer.Dispose();
|
||||||
|
|
||||||
if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) &&
|
if (Gd.Capabilities.VertexBufferAlignment < 2 &&
|
||||||
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
|
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
|
||||||
{
|
{
|
||||||
buffer = new VertexBufferState(
|
buffer = new VertexBufferState(
|
||||||
@@ -1183,6 +1182,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return Math.Clamp(value, 0f, 1f);
|
return Math.Clamp(value, 0f, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DynamicState.ViewportsCount = (uint)count;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var viewport = viewports[i];
|
var viewport = viewports[i];
|
||||||
@@ -1196,8 +1197,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Clamp(viewport.DepthFar)));
|
Clamp(viewport.DepthFar)));
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicState.ViewportsCount = count;
|
|
||||||
|
|
||||||
float disableTransformF = disableTransform ? 1.0f : 0.0f;
|
float disableTransformF = disableTransform ? 1.0f : 0.0f;
|
||||||
if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform)
|
if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform)
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
int attachmentCount = 0;
|
int attachmentCount = 0;
|
||||||
int colorCount = 0;
|
int colorCount = 0;
|
||||||
|
int maxColorAttachmentIndex = -1;
|
||||||
|
|
||||||
for (int i = 0; i < state.AttachmentEnable.Length; i++)
|
for (int i = 0; i < state.AttachmentEnable.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -36,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
attachmentIndices[attachmentCount++] = i;
|
attachmentIndices[attachmentCount++] = i;
|
||||||
colorCount++;
|
colorCount++;
|
||||||
|
maxColorAttachmentIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +76,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (colorAttachmentsCount != 0)
|
if (colorAttachmentsCount != 0)
|
||||||
{
|
{
|
||||||
int maxAttachmentIndex = Constants.MaxRenderTargets - 1;
|
subpass.ColorAttachmentCount = (uint)maxColorAttachmentIndex + 1;
|
||||||
subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1;
|
|
||||||
subpass.PColorAttachments = &attachmentReferences[0];
|
subpass.PColorAttachments = &attachmentReferences[0];
|
||||||
|
|
||||||
// Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
|
// Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
|
||||||
for (int i = 0; i <= maxAttachmentIndex; i++)
|
for (int i = 0; i <= maxColorAttachmentIndex; i++)
|
||||||
{
|
{
|
||||||
subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
|
subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
|
||||||
}
|
}
|
||||||
@@ -252,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
|
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
|
||||||
{
|
{
|
||||||
alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment;
|
alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support divisor > 1
|
// TODO: Support divisor > 1
|
||||||
|
@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private Array4<float> _blendConstants;
|
private Array4<float> _blendConstants;
|
||||||
|
|
||||||
public int ViewportsCount;
|
public uint ViewportsCount;
|
||||||
public Array16<Viewport> Viewports;
|
public Array16<Viewport> Viewports;
|
||||||
|
|
||||||
private enum DirtyFlags
|
private enum DirtyFlags
|
||||||
@@ -88,10 +88,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_dirty |= DirtyFlags.Viewport;
|
_dirty |= DirtyFlags.Viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetViewportsDirty()
|
public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount)
|
||||||
|
{
|
||||||
|
Viewports = viewports;
|
||||||
|
ViewportsCount = viewportsCount;
|
||||||
|
|
||||||
|
if (ViewportsCount != 0)
|
||||||
{
|
{
|
||||||
_dirty |= DirtyFlags.Viewport;
|
_dirty |= DirtyFlags.Viewport;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ForceAllDirty()
|
public void ForceAllDirty()
|
||||||
{
|
{
|
||||||
@@ -155,7 +161,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private void RecordViewport(Vk api, CommandBuffer commandBuffer)
|
private void RecordViewport(Vk api, CommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
api.CmdSetViewport(commandBuffer, 0, (uint)ViewportsCount, Viewports.AsSpan());
|
if (ViewportsCount != 0)
|
||||||
|
{
|
||||||
|
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
||||||
|
|
||||||
private readonly List<QueryPool> _activeQueries;
|
private readonly List<(QueryPool, bool)> _activeQueries;
|
||||||
private CounterQueueEvent _activeConditionalRender;
|
private CounterQueueEvent _activeConditionalRender;
|
||||||
|
|
||||||
private readonly List<BufferedQuery> _pendingQueryCopies;
|
private readonly List<BufferedQuery> _pendingQueryCopies;
|
||||||
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
||||||
{
|
{
|
||||||
_activeQueries = new List<QueryPool>();
|
_activeQueries = new List<(QueryPool, bool)>();
|
||||||
_pendingQueryCopies = new();
|
_pendingQueryCopies = new();
|
||||||
|
|
||||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
@@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AutoFlush.RegisterFlush(DrawCount);
|
AutoFlush.RegisterFlush(DrawCount);
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
foreach (var queryPool in _activeQueries)
|
foreach ((var queryPool, _) in _activeQueries)
|
||||||
{
|
{
|
||||||
Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0);
|
Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0);
|
||||||
}
|
}
|
||||||
@@ -220,10 +220,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
// Restore per-command buffer state.
|
// Restore per-command buffer state.
|
||||||
|
|
||||||
foreach (var queryPool in _activeQueries)
|
foreach ((var queryPool, var isOcclusion) in _activeQueries)
|
||||||
{
|
{
|
||||||
|
bool isPrecise = Gd.Capabilities.SupportsPreciseOcclusionQueries && isOcclusion;
|
||||||
|
|
||||||
Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1);
|
Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1);
|
||||||
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
|
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, isPrecise ? QueryControlFlags.PreciseBit : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.ResetCounterPool();
|
Gd.ResetCounterPool();
|
||||||
@@ -231,7 +233,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Restore();
|
Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool)
|
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool isOcclusion, bool fromSamplePool)
|
||||||
{
|
{
|
||||||
if (needsReset)
|
if (needsReset)
|
||||||
{
|
{
|
||||||
@@ -247,16 +249,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
|
bool isPrecise = Gd.Capabilities.SupportsPreciseOcclusionQueries && isOcclusion;
|
||||||
|
Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, isPrecise ? QueryControlFlags.PreciseBit : 0);
|
||||||
|
|
||||||
_activeQueries.Add(pool);
|
_activeQueries.Add((pool, isOcclusion));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndQuery(QueryPool pool)
|
public void EndQuery(QueryPool pool)
|
||||||
{
|
{
|
||||||
Gd.Api.CmdEndQuery(CommandBuffer, pool, 0);
|
Gd.Api.CmdEndQuery(CommandBuffer, pool, 0);
|
||||||
|
|
||||||
_activeQueries.Remove(pool);
|
for (int i = 0; i < _activeQueries.Count; i++)
|
||||||
|
{
|
||||||
|
if (_activeQueries[i].Item1.Handle == pool.Handle)
|
||||||
|
{
|
||||||
|
_activeQueries.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyQueryResults(BufferedQuery query)
|
public void CopyQueryResults(BufferedQuery query)
|
||||||
@@ -271,7 +281,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
protected override void SignalAttachmentChange()
|
protected override void SignalAttachmentChange()
|
||||||
{
|
{
|
||||||
if (AutoFlush.ShouldFlush(DrawCount))
|
if (AutoFlush.ShouldFlushAttachmentChange(DrawCount))
|
||||||
{
|
{
|
||||||
FlushCommandsImpl();
|
FlushCommandsImpl();
|
||||||
}
|
}
|
||||||
|
@@ -100,7 +100,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
|||||||
if (_isSupported)
|
if (_isSupported)
|
||||||
{
|
{
|
||||||
bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
|
bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
|
||||||
_pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null);
|
bool isOcclusion = _type == CounterType.SamplesPassed;
|
||||||
|
_pipeline.BeginQuery(this, _queryPool, needsReset, isOcclusion, isOcclusion && resetSequence != null);
|
||||||
}
|
}
|
||||||
_resetSequence = null;
|
_resetSequence = null;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout (binding = 0, set = 2) uniform sampler2DMS tex;
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 tex_coord;
|
||||||
|
layout (location = 0) out vec4 colour;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
colour = texelFetch(tex, ivec2(tex_coord * vec2(textureSize(tex).xy)), gl_SampleID);
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout (binding = 0, set = 2) uniform sampler2DMS texDepth;
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 tex_coord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragDepth = texelFetch(texDepth, ivec2(tex_coord * vec2(textureSize(texDepth).xy)), gl_SampleID).r;
|
||||||
|
}
|
@@ -329,6 +329,61 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
|
|||||||
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly byte[] ColorBlitMsFragmentShaderSource = new byte[]
|
||||||
|
{
|
||||||
|
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
||||||
|
0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
|
||||||
|
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00,
|
||||||
|
0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F,
|
||||||
|
0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00,
|
||||||
|
0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x47, 0x00, 0x04, 0x00,
|
||||||
|
0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
|
||||||
|
0x0D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
|
||||||
|
0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
|
||||||
|
0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
|
||||||
|
0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
|
||||||
|
0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00,
|
||||||
|
0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||||
|
0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00,
|
||||||
|
0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
|
||||||
|
0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
|
||||||
|
0x11, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
|
0x0E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
|
||||||
|
0x14, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
|
||||||
|
0x12, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||||
|
0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
|
||||||
|
0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
|
||||||
|
0x1E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||||
|
0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
|
||||||
|
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly byte[] ColorBlitVertexShaderSource = new byte[]
|
public static readonly byte[] ColorBlitVertexShaderSource = new byte[]
|
||||||
{
|
{
|
||||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3F, 0x00, 0x00, 0x00,
|
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3F, 0x00, 0x00, 0x00,
|
||||||
@@ -1502,6 +1557,64 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
|
|||||||
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly byte[] DepthBlitMsFragmentShaderSource = new byte[]
|
||||||
|
{
|
||||||
|
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
||||||
|
0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
|
||||||
|
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46,
|
||||||
|
0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
||||||
|
0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72,
|
||||||
|
0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53,
|
||||||
|
0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00,
|
||||||
|
0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
|
||||||
|
0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00,
|
||||||
|
0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||||||
|
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
|
||||||
|
0x68, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
|
||||||
|
0x6F, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||||
|
0x85, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
|
||||||
|
0x1B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
|
||||||
|
0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
|
||||||
|
0x1D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
|
||||||
|
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
|
||||||
|
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly byte[] StencilBlitFragmentShaderSource = new byte[]
|
public static readonly byte[] StencilBlitFragmentShaderSource = new byte[]
|
||||||
{
|
{
|
||||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00,
|
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||||
@@ -1549,5 +1662,68 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
|
|||||||
0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
|
0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
|
||||||
0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly byte[] StencilBlitMsFragmentShaderSource = new byte[]
|
||||||
|
{
|
||||||
|
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
||||||
|
0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
||||||
|
0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F,
|
||||||
|
0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65,
|
||||||
|
0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0xA3, 0x13, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65,
|
||||||
|
0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46,
|
||||||
|
0x72, 0x61, 0x67, 0x53, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x53,
|
||||||
|
0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
|
||||||
|
0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
||||||
|
0x1B, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x49, 0x44, 0x00,
|
||||||
|
0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00,
|
||||||
|
0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x47, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
|
||||||
|
0x1B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
|
||||||
|
0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x16, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
|
||||||
|
0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
|
||||||
|
0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
|
||||||
|
0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
|
||||||
|
0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
|
||||||
|
0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
|
||||||
|
0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||||
|
0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
|
||||||
|
0x16, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00,
|
||||||
|
0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
|
||||||
|
0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00,
|
||||||
|
0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x22, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
#version 450 core
|
||||||
|
|
||||||
|
#extension GL_ARB_shader_stencil_export : require
|
||||||
|
|
||||||
|
layout (binding = 0, set = 2) uniform isampler2DMS texStencil;
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 tex_coord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragStencilRefARB = texelFetch(texStencil, ivec2(tex_coord * vec2(textureSize(texStencil).xy)), gl_SampleID).r;
|
||||||
|
}
|
@@ -55,7 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public unsafe TextureStorage(
|
public unsafe TextureStorage(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
PhysicalDevice physicalDevice,
|
|
||||||
Device device,
|
Device device,
|
||||||
TextureCreateInfo info,
|
TextureCreateInfo info,
|
||||||
float scaleFactor,
|
float scaleFactor,
|
||||||
@@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
if (foreignAllocation == null)
|
if (foreignAllocation == null)
|
||||||
{
|
{
|
||||||
gd.Api.GetImageMemoryRequirements(device, _image, out var requirements);
|
gd.Api.GetImageMemoryRequirements(device, _image, out var requirements);
|
||||||
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(physicalDevice, requirements, DefaultImageMemoryFlags);
|
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, DefaultImageMemoryFlags);
|
||||||
|
|
||||||
if (allocation.Memory.Handle == 0UL)
|
if (allocation.Memory.Handle == 0UL)
|
||||||
{
|
{
|
||||||
@@ -173,7 +172,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel);
|
var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel);
|
||||||
|
|
||||||
storage = new TextureStorage(_gd, default, _device, info, ScaleFactor, _allocationAuto);
|
storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto);
|
||||||
|
|
||||||
_aliasedStorages.Add(format, storage);
|
_aliasedStorages.Add(format, storage);
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA);
|
var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA);
|
||||||
|
|
||||||
var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode);
|
var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode);
|
||||||
var aspectFlagsDepth = info.Format.ConvertAspectFlags(DepthStencilMode.Depth);
|
var aspectFlagsDepth = info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
||||||
var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
||||||
@@ -368,20 +368,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
|
bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
|
||||||
|
|
||||||
if (VulkanConfiguration.UseSlowSafeBlitOnAmd &&
|
if (VulkanConfiguration.UseSlowSafeBlitOnAmd && (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk))
|
||||||
(_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) &&
|
|
||||||
src.Info.Target == Target.Texture2D &&
|
|
||||||
dst.Info.Target == Target.Texture2D)
|
|
||||||
{
|
{
|
||||||
_gd.HelperShader.Blit(
|
_gd.HelperShader.Blit(
|
||||||
_gd,
|
_gd,
|
||||||
src,
|
src,
|
||||||
dst.GetIdentityImageView(),
|
dst,
|
||||||
dst.Width,
|
|
||||||
dst.Height,
|
|
||||||
dst.VkFormat,
|
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
|
layers,
|
||||||
|
levels,
|
||||||
isDepthOrStencil,
|
isDepthOrStencil,
|
||||||
linearFilter);
|
linearFilter);
|
||||||
|
|
||||||
@@ -501,7 +497,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return CreateViewImpl(info, firstLayer, firstLevel);
|
return CreateViewImpl(info, firstLayer, firstLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextureView CreateViewImpl(TextureCreateInfo info, int firstLayer, int firstLevel)
|
public TextureView CreateViewImpl(TextureCreateInfo info, int firstLayer, int firstLevel)
|
||||||
{
|
{
|
||||||
return new TextureView(_gd, _device, info, Storage, FirstLayer + firstLayer, FirstLevel + firstLevel);
|
return new TextureView(_gd, _device, info, Storage, FirstLayer + firstLayer, FirstLevel + firstLevel);
|
||||||
}
|
}
|
||||||
@@ -716,7 +712,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
for (int level = 0; level < levels; level++)
|
for (int level = 0; level < levels; level++)
|
||||||
{
|
{
|
||||||
int mipSize = GetBufferDataLength(Info.GetMipSize(dstLevel + level));
|
int mipSize = GetBufferDataLength(Info.GetMipSize2D(dstLevel + level) * dstLayers);
|
||||||
|
|
||||||
int endOffset = offset + mipSize;
|
int endOffset = offset + mipSize;
|
||||||
|
|
||||||
|
@@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public unsafe static class VulkanInitialization
|
public unsafe static class VulkanInitialization
|
||||||
{
|
{
|
||||||
private const uint InvalidIndex = uint.MaxValue;
|
private const uint InvalidIndex = uint.MaxValue;
|
||||||
|
private static uint MinimalVulkanVersion = Vk.Version11.Value;
|
||||||
|
private static uint MinimalInstanceVulkanVersion = Vk.Version12.Value;
|
||||||
|
private static uint MaximumVulkanVersion = Vk.Version12.Value;
|
||||||
private const string AppName = "Ryujinx.Graphics.Vulkan";
|
private const string AppName = "Ryujinx.Graphics.Vulkan";
|
||||||
private const int QueuesCount = 2;
|
private const int QueuesCount = 2;
|
||||||
|
|
||||||
@@ -33,7 +36,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_KHR_shader_float16_int8",
|
"VK_KHR_shader_float16_int8",
|
||||||
"VK_EXT_shader_subgroup_ballot",
|
"VK_EXT_shader_subgroup_ballot",
|
||||||
"VK_EXT_subgroup_size_control",
|
"VK_EXT_subgroup_size_control",
|
||||||
"VK_NV_geometry_shader_passthrough"
|
"VK_NV_geometry_shader_passthrough",
|
||||||
|
"VK_KHR_portability_subset", // By spec, we should enable this if present.
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string[] RequiredExtensions { get; } = new string[]
|
public static string[] RequiredExtensions { get; } = new string[]
|
||||||
@@ -99,7 +103,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ApplicationVersion = 1,
|
ApplicationVersion = 1,
|
||||||
PEngineName = (byte*)appName,
|
PEngineName = (byte*)appName,
|
||||||
EngineVersion = 1,
|
EngineVersion = 1,
|
||||||
ApiVersion = Vk.Version12.Value
|
ApiVersion = MaximumVulkanVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
||||||
@@ -224,7 +228,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ApplicationVersion = 1,
|
ApplicationVersion = 1,
|
||||||
PEngineName = (byte*)appName,
|
PEngineName = (byte*)appName,
|
||||||
EngineVersion = 1,
|
EngineVersion = 1,
|
||||||
ApiVersion = Vk.Version12.Value
|
ApiVersion = MaximumVulkanVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
var instanceCreateInfo = new InstanceCreateInfo
|
var instanceCreateInfo = new InstanceCreateInfo
|
||||||
@@ -239,6 +243,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
|
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
|
||||||
|
|
||||||
|
// We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
|
||||||
|
// If the instance doesn't support it, no device is going to be 1.1 compatible.
|
||||||
|
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
api.DestroyInstance(instance, null);
|
||||||
|
|
||||||
|
return Array.Empty<DeviceInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We currently assume that the instance is compatible with Vulkan 1.2
|
||||||
|
// TODO: Remove this once we relax our initialization codepaths.
|
||||||
|
uint instanceApiVerison = 0;
|
||||||
|
api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
|
||||||
|
|
||||||
|
if (instanceApiVerison < MinimalInstanceVulkanVersion)
|
||||||
|
{
|
||||||
|
api.DestroyInstance(instance, null);
|
||||||
|
|
||||||
|
return Array.Empty<DeviceInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
Marshal.FreeHGlobal(appName);
|
Marshal.FreeHGlobal(appName);
|
||||||
|
|
||||||
uint physicalDeviceCount;
|
uint physicalDeviceCount;
|
||||||
@@ -259,6 +284,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var physicalDevice = physicalDevices[i];
|
var physicalDevice = physicalDevices[i];
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
||||||
|
|
||||||
|
if (properties.ApiVersion < MinimalVulkanVersion)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
devices[i] = new DeviceInfo(
|
devices[i] = new DeviceInfo(
|
||||||
StringFromIdPair(properties.VendorID, properties.DeviceID),
|
StringFromIdPair(properties.VendorID, properties.DeviceID),
|
||||||
VendorUtils.GetNameFromId(properties.VendorID),
|
VendorUtils.GetNameFromId(properties.VendorID),
|
||||||
|
@@ -234,10 +234,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
|
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
|
||||||
|
|
||||||
var portabilityFlags = PortabilitySubsetFlags.None;
|
var portabilityFlags = PortabilitySubsetFlags.None;
|
||||||
|
uint vertexBufferAlignment = 1;
|
||||||
|
|
||||||
if (usePortability)
|
if (usePortability)
|
||||||
{
|
{
|
||||||
portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0;
|
vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment;
|
||||||
|
|
||||||
portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
|
portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
|
||||||
portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
|
portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
|
||||||
portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
|
portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
|
||||||
@@ -278,9 +280,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
||||||
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
||||||
supportedSampleCounts,
|
supportedSampleCounts,
|
||||||
portabilityFlags);
|
portabilityFlags,
|
||||||
|
vertexBufferAlignment);
|
||||||
|
|
||||||
MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount);
|
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
|
||||||
|
|
||||||
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||||
|
|
||||||
@@ -290,7 +293,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
BackgroundResources = new BackgroundResources(this, _device);
|
BackgroundResources = new BackgroundResources(this, _device);
|
||||||
|
|
||||||
BufferManager = new BufferManager(this, _physicalDevice, _device);
|
BufferManager = new BufferManager(this, _device);
|
||||||
|
|
||||||
_syncManager = new SyncManager(this, _device);
|
_syncManager = new SyncManager(this, _device);
|
||||||
_pipeline = new PipelineFull(this, _device);
|
_pipeline = new PipelineFull(this, _device);
|
||||||
@@ -388,7 +391,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale)
|
internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale)
|
||||||
{
|
{
|
||||||
return new TextureStorage(this, _physicalDevice, _device, info, scale);
|
return new TextureStorage(this, _device, info, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteBuffer(BufferHandle buffer)
|
public void DeleteBuffer(BufferHandle buffer)
|
||||||
@@ -636,11 +639,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PrintGpuInformation();
|
PrintGpuInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
|
internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
|
||||||
{
|
{
|
||||||
if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B))
|
if (Capabilities.VertexBufferAlignment > 1)
|
||||||
{
|
{
|
||||||
alignment = 4;
|
alignment = (int)Capabilities.VertexBufferAlignment;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -342,7 +342,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_swapchainImageViews[nextImage],
|
_swapchainImageViews[nextImage],
|
||||||
_width,
|
_width,
|
||||||
_height,
|
_height,
|
||||||
|
1,
|
||||||
_format,
|
_format,
|
||||||
|
false,
|
||||||
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
||||||
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
||||||
true,
|
true,
|
||||||
|
@@ -229,7 +229,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ncaId = BitConverter.ToString(cnmt.ContentEntries[0].NcaId).Replace("-", "").ToLower();
|
string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower();
|
||||||
|
|
||||||
AddAocItem(cnmt.TitleId, containerPath, $"{ncaId}.nca", true);
|
AddAocItem(cnmt.TitleId, containerPath, $"{ncaId}.nca", true);
|
||||||
}
|
}
|
||||||
|
@@ -696,8 +696,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
var buildIds = programs.Select(p => p switch
|
var buildIds = programs.Select(p => p switch
|
||||||
{
|
{
|
||||||
NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()).Replace("-", "").TrimEnd('0'),
|
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'),
|
||||||
NroExecutable nro => BitConverter.ToString(nro.Header.BuildId).Replace("-", "").TrimEnd('0'),
|
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'),
|
||||||
_ => string.Empty
|
_ => string.Empty
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
@@ -51,11 +51,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
|||||||
|
|
||||||
var payload = new JwtPayload
|
var payload = new JwtPayload
|
||||||
{
|
{
|
||||||
{ "sub", BitConverter.ToString(rawUserId).Replace("-", "").ToLower() },
|
{ "sub", Convert.ToHexString(rawUserId).ToLower() },
|
||||||
{ "aud", "ed9e2f05d286f7b8" },
|
{ "aud", "ed9e2f05d286f7b8" },
|
||||||
{ "di", BitConverter.ToString(deviceId).Replace("-", "").ToLower() },
|
{ "di", Convert.ToHexString(deviceId).ToLower() },
|
||||||
{ "sn", "XAW10000000000" },
|
{ "sn", "XAW10000000000" },
|
||||||
{ "bs:did", BitConverter.ToString(deviceAccountId).Replace("-", "").ToLower() },
|
{ "bs:did", Convert.ToHexString(deviceAccountId).ToLower() },
|
||||||
{ "iss", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com" },
|
{ "iss", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com" },
|
||||||
{ "typ", "id_token" },
|
{ "typ", "id_token" },
|
||||||
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
|
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
|
||||||
|
@@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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.
|
// 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 hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId))).Remove(0x20);
|
||||||
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
|
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);
|
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
||||||
|
|
||||||
|
@@ -38,7 +38,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
|
|||||||
|
|
||||||
public static bool IsValidNpadIdType(NpadIdType npadIdType)
|
public static bool IsValidNpadIdType(NpadIdType npadIdType)
|
||||||
{
|
{
|
||||||
return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown;
|
return (npadIdType >= NpadIdType.Player1 && npadIdType <= NpadIdType.Player8) ||
|
||||||
|
npadIdType == NpadIdType.Handheld ||
|
||||||
|
npadIdType == NpadIdType.Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -722,7 +722,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
for (int i = 0; i < supportedPlayerIds.Length; ++i)
|
for (int i = 0; i < supportedPlayerIds.Length; ++i)
|
||||||
{
|
{
|
||||||
if (supportedPlayerIds[i] >= 0)
|
if (HidUtils.IsValidNpadIdType(supportedPlayerIds[i]))
|
||||||
{
|
{
|
||||||
context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i]));
|
context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i]));
|
||||||
}
|
}
|
||||||
@@ -1101,7 +1101,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
|
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
|
||||||
{
|
{
|
||||||
if (npadIdType >= (NpadIdType.Player8 + 1) && npadIdType != NpadIdType.Handheld && npadIdType != NpadIdType.Unknown)
|
if (!HidUtils.IsValidNpadIdType(npadIdType))
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidNpadIdType;
|
return ResultCode.InvalidNpadIdType;
|
||||||
}
|
}
|
||||||
|
@@ -96,7 +96,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false)
|
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@@ -34,8 +34,8 @@ namespace Ryujinx.Memory.Tests
|
|||||||
private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity)
|
private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity)
|
||||||
{
|
{
|
||||||
return smart ?
|
return smart ?
|
||||||
_tracking.BeginSmartGranularTracking(address, size, granularity) :
|
_tracking.BeginSmartGranularTracking(address, size, granularity, 0) :
|
||||||
(IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity);
|
(IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RandomOrder(Random random, List<int> indices, Action<int> action)
|
private void RandomOrder(Random random, List<int> indices, Action<int> action)
|
||||||
@@ -294,7 +294,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
|
|
||||||
bool[] actionsTriggered = new bool[3];
|
bool[] actionsTriggered = new bool[3];
|
||||||
|
|
||||||
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize);
|
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0);
|
||||||
PreparePages(granular, 3, PageSize * 3);
|
PreparePages(granular, 3, PageSize * 3);
|
||||||
|
|
||||||
// Write to the second handle in the multiregion.
|
// Write to the second handle in the multiregion.
|
||||||
@@ -307,7 +307,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize);
|
singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize, 0);
|
||||||
singlePages[i].Reprotect();
|
singlePages[i].Reprotect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2);
|
doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2, 0);
|
||||||
doublePages[i].Reprotect();
|
doublePages[i].Reprotect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +340,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
doublePages
|
doublePages
|
||||||
};
|
};
|
||||||
|
|
||||||
MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize);
|
MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize, 0);
|
||||||
|
|
||||||
bool[] expectedDirty = new bool[]
|
bool[] expectedDirty = new bool[]
|
||||||
{
|
{
|
||||||
@@ -405,7 +405,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
{
|
{
|
||||||
bool actionTriggered = false;
|
bool actionTriggered = false;
|
||||||
|
|
||||||
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize);
|
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0);
|
||||||
PreparePages(granular, 3, PageSize * 3);
|
PreparePages(granular, 3, PageSize * 3);
|
||||||
|
|
||||||
// Add a precise action to the second and third handle in the multiregion.
|
// Add a precise action to the second and third handle in the multiregion.
|
||||||
|
@@ -44,7 +44,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void SingleRegion()
|
public void SingleRegion()
|
||||||
{
|
{
|
||||||
RegionHandle handle = _tracking.BeginTracking(0, PageSize);
|
RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
|
||||||
(ulong address, ulong size)? readTrackingTriggered = null;
|
(ulong address, ulong size)? readTrackingTriggered = null;
|
||||||
handle.RegisterAction((address, size) =>
|
handle.RegisterAction((address, size) =>
|
||||||
{
|
{
|
||||||
@@ -97,7 +97,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void OverlappingRegions()
|
public void OverlappingRegions()
|
||||||
{
|
{
|
||||||
RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16);
|
RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16, 0);
|
||||||
allHandle.Reprotect();
|
allHandle.Reprotect();
|
||||||
|
|
||||||
(ulong address, ulong size)? readTrackingTriggeredAll = null;
|
(ulong address, ulong size)? readTrackingTriggeredAll = null;
|
||||||
@@ -116,7 +116,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize);
|
containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0);
|
||||||
containedHandles[i].Reprotect();
|
containedHandles[i].Reprotect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize;
|
ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize;
|
||||||
ulong alignedSize = alignedEnd - alignedStart;
|
ulong alignedSize = alignedEnd - alignedStart;
|
||||||
|
|
||||||
RegionHandle handle = _tracking.BeginTracking(address, size);
|
RegionHandle handle = _tracking.BeginTracking(address, size, 0);
|
||||||
|
|
||||||
// Anywhere inside the pages the region is contained on should trigger.
|
// Anywhere inside the pages the region is contained on should trigger.
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
|
|
||||||
for (int i = 0; i < handles.Length; i++)
|
for (int i = 0; i < handles.Length; i++)
|
||||||
{
|
{
|
||||||
handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize);
|
handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0);
|
||||||
handles[i].Reprotect();
|
handles[i].Reprotect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
Random random = new Random(randSeed + 512);
|
Random random = new Random(randSeed + 512);
|
||||||
while (Stopwatch.GetTimestamp() < finishedTime)
|
while (Stopwatch.GetTimestamp() < finishedTime)
|
||||||
{
|
{
|
||||||
RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536));
|
RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536), 0);
|
||||||
|
|
||||||
handle.Dispose();
|
handle.Dispose();
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
// Read actions should only be triggered once for each registration.
|
// Read actions should only be triggered once for each registration.
|
||||||
// The implementation should use an interlocked exchange to make sure other threads can't get the action.
|
// The implementation should use an interlocked exchange to make sure other threads can't get the action.
|
||||||
|
|
||||||
RegionHandle handle = _tracking.BeginTracking(0, PageSize);
|
RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
|
||||||
|
|
||||||
int triggeredCount = 0;
|
int triggeredCount = 0;
|
||||||
int registeredCount = 0;
|
int registeredCount = 0;
|
||||||
@@ -359,7 +359,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
{
|
{
|
||||||
// Ensure that disposed handles correctly remove their virtual and physical regions.
|
// Ensure that disposed handles correctly remove their virtual and physical regions.
|
||||||
|
|
||||||
RegionHandle handle = _tracking.BeginTracking(0, PageSize);
|
RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
|
||||||
handle.Reprotect();
|
handle.Reprotect();
|
||||||
|
|
||||||
Assert.AreEqual(1, _tracking.GetRegionCount());
|
Assert.AreEqual(1, _tracking.GetRegionCount());
|
||||||
@@ -372,8 +372,8 @@ namespace Ryujinx.Memory.Tests
|
|||||||
// We expect there to be three regions after creating both, one for the small region and two covering the big one around it.
|
// We expect there to be three regions after creating both, one for the small region and two covering the big one around it.
|
||||||
// Regions are always split to avoid overlapping, which is why there are three instead of two.
|
// Regions are always split to avoid overlapping, which is why there are three instead of two.
|
||||||
|
|
||||||
RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize);
|
RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize, 0);
|
||||||
RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4);
|
RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4, 0);
|
||||||
|
|
||||||
Assert.AreEqual(3, _tracking.GetRegionCount());
|
Assert.AreEqual(3, _tracking.GetRegionCount());
|
||||||
|
|
||||||
@@ -398,7 +398,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
protection = newProtection;
|
protection = newProtection;
|
||||||
};
|
};
|
||||||
|
|
||||||
RegionHandle handle = _tracking.BeginTracking(0, PageSize);
|
RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
|
||||||
|
|
||||||
// After creating the handle, there is no protection yet.
|
// After creating the handle, there is no protection yet.
|
||||||
Assert.AreEqual(MemoryPermission.ReadAndWrite, protection);
|
Assert.AreEqual(MemoryPermission.ReadAndWrite, protection);
|
||||||
@@ -453,7 +453,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void PreciseAction()
|
public void PreciseAction()
|
||||||
{
|
{
|
||||||
RegionHandle handle = _tracking.BeginTracking(0, PageSize);
|
RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
|
||||||
|
|
||||||
(ulong address, ulong size, bool write)? preciseTriggered = null;
|
(ulong address, ulong size, bool write)? preciseTriggered = null;
|
||||||
handle.RegisterPreciseAction((address, size, write) =>
|
handle.RegisterPreciseAction((address, size, write) =>
|
||||||
|
@@ -462,7 +462,7 @@ namespace Ryujinx.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false)
|
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||||
{
|
{
|
||||||
// Only the ARM Memory Manager has tracking for now.
|
// Only the ARM Memory Manager has tracking for now.
|
||||||
}
|
}
|
||||||
|
@@ -175,7 +175,8 @@ namespace Ryujinx.Memory
|
|||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="write">True if the region was written, false if read</param>
|
/// <param name="write">True if the region was written, false if read</param>
|
||||||
/// <param name="precise">True if the access is precise, false otherwise</param>
|
/// <param name="precise">True if the access is precise, false otherwise</param>
|
||||||
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false);
|
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
|
||||||
|
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reprotect a region of virtual memory for tracking.
|
/// Reprotect a region of virtual memory for tracking.
|
||||||
|
@@ -50,7 +50,8 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="address">Address accessed</param>
|
/// <param name="address">Address accessed</param>
|
||||||
/// <param name="size">Size of the region affected in bytes</param>
|
/// <param name="size">Size of the region affected in bytes</param>
|
||||||
/// <param name="write">Whether the region was written to or read</param>
|
/// <param name="write">Whether the region was written to or read</param>
|
||||||
public abstract void Signal(ulong address, ulong size, bool write);
|
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
|
||||||
|
public abstract void Signal(ulong address, ulong size, bool write, int? exemptId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained.
|
/// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained.
|
||||||
@@ -58,7 +59,8 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="address">Address accessed</param>
|
/// <param name="address">Address accessed</param>
|
||||||
/// <param name="size">Size of the region affected in bytes</param>
|
/// <param name="size">Size of the region affected in bytes</param>
|
||||||
/// <param name="write">Whether the region was written to or read</param>
|
/// <param name="write">Whether the region was written to or read</param>
|
||||||
public abstract void SignalPrecise(ulong address, ulong size, bool write);
|
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
|
||||||
|
public abstract void SignalPrecise(ulong address, ulong size, bool write, int? exemptId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Split this region into two, around the specified address.
|
/// Split this region into two, around the specified address.
|
||||||
|
@@ -136,10 +136,11 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
|
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
|
||||||
/// <param name="granularity">Desired granularity of write tracking</param>
|
/// <param name="granularity">Desired granularity of write tracking</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
return new MultiRegionHandle(this, address, size, handles, granularity);
|
return new MultiRegionHandle(this, address, size, handles, granularity, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -148,12 +149,13 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="granularity">Desired granularity of write tracking</param>
|
/// <param name="granularity">Desired granularity of write tracking</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity)
|
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
(address, size) = PageAlign(address, size);
|
(address, size) = PageAlign(address, size);
|
||||||
|
|
||||||
return new SmartMultiRegionHandle(this, address, size, granularity);
|
return new SmartMultiRegionHandle(this, address, size, granularity, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -161,14 +163,16 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">CPU virtual address of the region</param>
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
public RegionHandle BeginTracking(ulong address, ulong size)
|
public RegionHandle BeginTracking(ulong address, ulong size, int id)
|
||||||
{
|
{
|
||||||
var (paAddress, paSize) = PageAlign(address, size);
|
var (paAddress, paSize) = PageAlign(address, size);
|
||||||
|
|
||||||
lock (TrackingLock)
|
lock (TrackingLock)
|
||||||
{
|
{
|
||||||
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, _memoryManager.IsRangeMapped(address, size));
|
bool mapped = _memoryManager.IsRangeMapped(address, size);
|
||||||
|
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped);
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
@@ -181,28 +185,31 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="size">Size of the region</param>
|
/// <param name="size">Size of the region</param>
|
||||||
/// <param name="bitmap">The bitmap owning the dirty flag for this handle</param>
|
/// <param name="bitmap">The bitmap owning the dirty flag for this handle</param>
|
||||||
/// <param name="bit">The bit of this handle within the dirty flag</param>
|
/// <param name="bit">The bit of this handle within the dirty flag</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <returns>The memory tracking handle</returns>
|
/// <returns>The memory tracking handle</returns>
|
||||||
internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit)
|
internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit, int id)
|
||||||
{
|
{
|
||||||
var (paAddress, paSize) = PageAlign(address, size);
|
var (paAddress, paSize) = PageAlign(address, size);
|
||||||
|
|
||||||
lock (TrackingLock)
|
lock (TrackingLock)
|
||||||
{
|
{
|
||||||
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size));
|
bool mapped = _memoryManager.IsRangeMapped(address, size);
|
||||||
|
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped);
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signal that a virtual memory event happened at the given location (one byte).
|
/// Signal that a virtual memory event happened at the given location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">Virtual address accessed</param>
|
/// <param name="address">Virtual address accessed</param>
|
||||||
/// <param name="write">Whether the address was written to or read</param>
|
/// <param name="size">Size of the region affected in bytes</param>
|
||||||
|
/// <param name="write">Whether the region was written to or read</param>
|
||||||
/// <returns>True if the event triggered any tracking regions, false otherwise</returns>
|
/// <returns>True if the event triggered any tracking regions, false otherwise</returns>
|
||||||
public bool VirtualMemoryEventTracking(ulong address, bool write)
|
public bool VirtualMemoryEvent(ulong address, ulong size, bool write)
|
||||||
{
|
{
|
||||||
return VirtualMemoryEvent(address, 1, write);
|
return VirtualMemoryEvent(address, size, write, precise: false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -214,8 +221,9 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="size">Size of the region affected in bytes</param>
|
/// <param name="size">Size of the region affected in bytes</param>
|
||||||
/// <param name="write">Whether the region was written to or read</param>
|
/// <param name="write">Whether the region was written to or read</param>
|
||||||
/// <param name="precise">True if the access is precise, false otherwise</param>
|
/// <param name="precise">True if the access is precise, false otherwise</param>
|
||||||
|
/// <param name="exemptId">Optional ID that of the handles that should not be signalled</param>
|
||||||
/// <returns>True if the event triggered any tracking regions, false otherwise</returns>
|
/// <returns>True if the event triggered any tracking regions, false otherwise</returns>
|
||||||
public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise = false)
|
public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise, int? exemptId = null)
|
||||||
{
|
{
|
||||||
// Look up the virtual region using the region list.
|
// Look up the virtual region using the region list.
|
||||||
// Signal up the chain to relevant handles.
|
// Signal up the chain to relevant handles.
|
||||||
@@ -250,11 +258,11 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
if (precise)
|
if (precise)
|
||||||
{
|
{
|
||||||
region.SignalPrecise(address, size, write);
|
region.SignalPrecise(address, size, write, exemptId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
region.Signal(address, size, write);
|
region.Signal(address, size, write, exemptId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,13 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
public bool Dirty { get; private set; } = true;
|
public bool Dirty { get; private set; } = true;
|
||||||
|
|
||||||
internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
internal MultiRegionHandle(
|
||||||
|
MemoryTracking tracking,
|
||||||
|
ulong address,
|
||||||
|
ulong size,
|
||||||
|
IEnumerable<IRegionHandle> handles,
|
||||||
|
ulong granularity,
|
||||||
|
int id)
|
||||||
{
|
{
|
||||||
_handles = new RegionHandle[(size + granularity - 1) / granularity];
|
_handles = new RegionHandle[(size + granularity - 1) / granularity];
|
||||||
Granularity = granularity;
|
Granularity = granularity;
|
||||||
@@ -55,7 +61,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
// Fill any gap left before this handle.
|
// Fill any gap left before this handle.
|
||||||
while (i < startIndex)
|
while (i < startIndex)
|
||||||
{
|
{
|
||||||
RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
|
RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
|
||||||
fillHandle.Parent = this;
|
fillHandle.Parent = this;
|
||||||
_handles[i++] = fillHandle;
|
_handles[i++] = fillHandle;
|
||||||
}
|
}
|
||||||
@@ -76,7 +82,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
while (i < endIndex)
|
while (i < endIndex)
|
||||||
{
|
{
|
||||||
RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
|
RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
|
||||||
splitHandle.Parent = this;
|
splitHandle.Parent = this;
|
||||||
|
|
||||||
splitHandle.Reprotect(handle.Dirty);
|
splitHandle.Reprotect(handle.Dirty);
|
||||||
@@ -99,7 +105,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
// Fill any remaining space with new handles.
|
// Fill any remaining space with new handles.
|
||||||
while (i < _handles.Length)
|
while (i < _handles.Length)
|
||||||
{
|
{
|
||||||
RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
|
RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
|
||||||
handle.Parent = this;
|
handle.Parent = this;
|
||||||
_handles[i++] = handle;
|
_handles[i++] = handle;
|
||||||
}
|
}
|
||||||
|
@@ -15,12 +15,12 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// If more than this number of checks have been performed on a dirty flag since its last reprotect,
|
/// If more than this number of checks have been performed on a dirty flag since its last reprotect,
|
||||||
/// then it is dirtied infrequently.
|
/// then it is dirtied infrequently.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static int CheckCountForInfrequent = 3;
|
private const int CheckCountForInfrequent = 3;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of frequent dirty/consume in a row to make this handle volatile.
|
/// Number of frequent dirty/consume in a row to make this handle volatile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static int VolatileThreshold = 5;
|
private const int VolatileThreshold = 5;
|
||||||
|
|
||||||
public bool Dirty
|
public bool Dirty
|
||||||
{
|
{
|
||||||
@@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal int SequenceNumber { get; set; }
|
internal int SequenceNumber { get; set; }
|
||||||
|
internal int Id { get; }
|
||||||
|
|
||||||
public bool Unmapped { get; private set; }
|
public bool Unmapped { get; private set; }
|
||||||
|
|
||||||
@@ -97,14 +98,26 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="realSize">The real, unaligned size of the handle</param>
|
/// <param name="realSize">The real, unaligned size of the handle</param>
|
||||||
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
|
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
|
||||||
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
|
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <param name="mapped">True if the region handle starts mapped</param>
|
/// <param name="mapped">True if the region handle starts mapped</param>
|
||||||
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, ConcurrentBitmap bitmap, int bit, bool mapped = true)
|
internal RegionHandle(
|
||||||
|
MemoryTracking tracking,
|
||||||
|
ulong address,
|
||||||
|
ulong size,
|
||||||
|
ulong realAddress,
|
||||||
|
ulong realSize,
|
||||||
|
ConcurrentBitmap bitmap,
|
||||||
|
int bit,
|
||||||
|
int id,
|
||||||
|
bool mapped = true)
|
||||||
{
|
{
|
||||||
Bitmap = bitmap;
|
Bitmap = bitmap;
|
||||||
DirtyBit = bit;
|
DirtyBit = bit;
|
||||||
|
|
||||||
Dirty = mapped;
|
Dirty = mapped;
|
||||||
|
|
||||||
|
Id = id;
|
||||||
|
|
||||||
Unmapped = !mapped;
|
Unmapped = !mapped;
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
@@ -131,11 +144,14 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// <param name="size">Size of the region to track</param>
|
/// <param name="size">Size of the region to track</param>
|
||||||
/// <param name="realAddress">The real, unaligned address of the handle</param>
|
/// <param name="realAddress">The real, unaligned address of the handle</param>
|
||||||
/// <param name="realSize">The real, unaligned size of the handle</param>
|
/// <param name="realSize">The real, unaligned size of the handle</param>
|
||||||
|
/// <param name="id">Handle ID</param>
|
||||||
/// <param name="mapped">True if the region handle starts mapped</param>
|
/// <param name="mapped">True if the region handle starts mapped</param>
|
||||||
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, bool mapped = true)
|
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, int id, bool mapped = true)
|
||||||
{
|
{
|
||||||
Bitmap = new ConcurrentBitmap(1, mapped);
|
Bitmap = new ConcurrentBitmap(1, mapped);
|
||||||
|
|
||||||
|
Id = id;
|
||||||
|
|
||||||
Unmapped = !mapped;
|
Unmapped = !mapped;
|
||||||
|
|
||||||
Address = address;
|
Address = address;
|
||||||
|
@@ -18,10 +18,11 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
private readonly ulong _granularity;
|
private readonly ulong _granularity;
|
||||||
private readonly ulong _size;
|
private readonly ulong _size;
|
||||||
private MemoryTracking _tracking;
|
private MemoryTracking _tracking;
|
||||||
|
private readonly int _id;
|
||||||
|
|
||||||
public bool Dirty { get; private set; } = true;
|
public bool Dirty { get; private set; } = true;
|
||||||
|
|
||||||
internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity)
|
internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id)
|
||||||
{
|
{
|
||||||
// For this multi-region handle, the handle list starts empty.
|
// For this multi-region handle, the handle list starts empty.
|
||||||
// As regions are queried, they are added to the _handles array at their start index.
|
// As regions are queried, they are added to the _handles array at their start index.
|
||||||
@@ -34,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
_address = address;
|
_address = address;
|
||||||
_size = size;
|
_size = size;
|
||||||
|
_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SignalWrite()
|
public void SignalWrite()
|
||||||
@@ -102,7 +104,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
RegionSignal signal = handle.PreAction;
|
RegionSignal signal = handle.PreAction;
|
||||||
handle.Dispose();
|
handle.Dispose();
|
||||||
|
|
||||||
RegionHandle splitLow = _tracking.BeginTracking(address, size);
|
RegionHandle splitLow = _tracking.BeginTracking(address, size, _id);
|
||||||
splitLow.Parent = this;
|
splitLow.Parent = this;
|
||||||
if (signal != null)
|
if (signal != null)
|
||||||
{
|
{
|
||||||
@@ -110,7 +112,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
_handles[handleIndex] = splitLow;
|
_handles[handleIndex] = splitLow;
|
||||||
|
|
||||||
RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size);
|
RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id);
|
||||||
splitHigh.Parent = this;
|
splitHigh.Parent = this;
|
||||||
if (signal != null)
|
if (signal != null)
|
||||||
{
|
{
|
||||||
@@ -145,7 +147,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
if (handle != null)
|
if (handle != null)
|
||||||
{
|
{
|
||||||
// Fill up to the found handle.
|
// Fill up to the found handle.
|
||||||
handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle));
|
handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id);
|
||||||
handle.Parent = this;
|
handle.Parent = this;
|
||||||
_handles[startHandle] = handle;
|
_handles[startHandle] = handle;
|
||||||
return;
|
return;
|
||||||
@@ -153,7 +155,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Can fill the whole range.
|
// Can fill the whole range.
|
||||||
_handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle));
|
_handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id);
|
||||||
_handles[startHandle].Parent = this;
|
_handles[startHandle].Parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,28 +19,36 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
_tracking = tracking;
|
_tracking = tracking;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Signal(ulong address, ulong size, bool write)
|
/// <inheritdoc/>
|
||||||
|
public override void Signal(ulong address, ulong size, bool write, int? exemptId)
|
||||||
{
|
{
|
||||||
IList<RegionHandle> handles = Handles;
|
IList<RegionHandle> handles = Handles;
|
||||||
|
|
||||||
for (int i = 0; i < handles.Count; i++)
|
for (int i = 0; i < handles.Count; i++)
|
||||||
|
{
|
||||||
|
if (exemptId == null || handles[i].Id != exemptId.Value)
|
||||||
{
|
{
|
||||||
handles[i].Signal(address, size, write, ref handles);
|
handles[i].Signal(address, size, write, ref handles);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpdateProtection();
|
UpdateProtection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SignalPrecise(ulong address, ulong size, bool write)
|
/// <inheritdoc/>
|
||||||
|
public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId)
|
||||||
{
|
{
|
||||||
IList<RegionHandle> handles = Handles;
|
IList<RegionHandle> handles = Handles;
|
||||||
|
|
||||||
bool allPrecise = true;
|
bool allPrecise = true;
|
||||||
|
|
||||||
for (int i = 0; i < handles.Count; i++)
|
for (int i = 0; i < handles.Count; i++)
|
||||||
|
{
|
||||||
|
if (exemptId == null || handles[i].Id != exemptId.Value)
|
||||||
{
|
{
|
||||||
allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
|
allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only update protection if a regular signal handler was called.
|
// Only update protection if a regular signal handler was called.
|
||||||
// This allows precise actions to skip reprotection costs if they want (they can still do it manually).
|
// This allows precise actions to skip reprotection costs if they want (they can still do it manually).
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
namespace Ryujinx.Tests.Unicorn
|
||||||
{
|
{
|
||||||
|
[Flags]
|
||||||
public enum MemoryPermission
|
public enum MemoryPermission
|
||||||
{
|
{
|
||||||
NONE = 0,
|
None = 0,
|
||||||
READ = 1,
|
Read = 1,
|
||||||
WRITE = 2,
|
Write = 2,
|
||||||
EXEC = 4,
|
Exec = 4,
|
||||||
ALL = 7,
|
All = 7,
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,20 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Arch
|
|
||||||
{
|
|
||||||
ARM = 1,
|
|
||||||
ARM64 = 2,
|
|
||||||
MIPS = 3,
|
|
||||||
X86 = 4,
|
|
||||||
PPC = 5,
|
|
||||||
SPARC = 6,
|
|
||||||
M68K = 7,
|
|
||||||
RISCV = 8,
|
|
||||||
S390X = 9,
|
|
||||||
TRICORE = 10,
|
|
||||||
MAX = 11,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,200 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Arm
|
|
||||||
{
|
|
||||||
|
|
||||||
// ARM CPU
|
|
||||||
|
|
||||||
CPU_ARM_926 = 0,
|
|
||||||
CPU_ARM_946 = 1,
|
|
||||||
CPU_ARM_1026 = 2,
|
|
||||||
CPU_ARM_1136_R2 = 3,
|
|
||||||
CPU_ARM_1136 = 4,
|
|
||||||
CPU_ARM_1176 = 5,
|
|
||||||
CPU_ARM_11MPCORE = 6,
|
|
||||||
CPU_ARM_CORTEX_M0 = 7,
|
|
||||||
CPU_ARM_CORTEX_M3 = 8,
|
|
||||||
CPU_ARM_CORTEX_M4 = 9,
|
|
||||||
CPU_ARM_CORTEX_M7 = 10,
|
|
||||||
CPU_ARM_CORTEX_M33 = 11,
|
|
||||||
CPU_ARM_CORTEX_R5 = 12,
|
|
||||||
CPU_ARM_CORTEX_R5F = 13,
|
|
||||||
CPU_ARM_CORTEX_A7 = 14,
|
|
||||||
CPU_ARM_CORTEX_A8 = 15,
|
|
||||||
CPU_ARM_CORTEX_A9 = 16,
|
|
||||||
CPU_ARM_CORTEX_A15 = 17,
|
|
||||||
CPU_ARM_TI925T = 18,
|
|
||||||
CPU_ARM_SA1100 = 19,
|
|
||||||
CPU_ARM_SA1110 = 20,
|
|
||||||
CPU_ARM_PXA250 = 21,
|
|
||||||
CPU_ARM_PXA255 = 22,
|
|
||||||
CPU_ARM_PXA260 = 23,
|
|
||||||
CPU_ARM_PXA261 = 24,
|
|
||||||
CPU_ARM_PXA262 = 25,
|
|
||||||
CPU_ARM_PXA270 = 26,
|
|
||||||
CPU_ARM_PXA270A0 = 27,
|
|
||||||
CPU_ARM_PXA270A1 = 28,
|
|
||||||
CPU_ARM_PXA270B0 = 29,
|
|
||||||
CPU_ARM_PXA270B1 = 30,
|
|
||||||
CPU_ARM_PXA270C0 = 31,
|
|
||||||
CPU_ARM_PXA270C5 = 32,
|
|
||||||
CPU_ARM_MAX = 33,
|
|
||||||
CPU_ARM_ENDING = 34,
|
|
||||||
|
|
||||||
// ARM registers
|
|
||||||
|
|
||||||
REG_INVALID = 0,
|
|
||||||
REG_APSR = 1,
|
|
||||||
REG_APSR_NZCV = 2,
|
|
||||||
REG_CPSR = 3,
|
|
||||||
REG_FPEXC = 4,
|
|
||||||
REG_FPINST = 5,
|
|
||||||
REG_FPSCR = 6,
|
|
||||||
REG_FPSCR_NZCV = 7,
|
|
||||||
REG_FPSID = 8,
|
|
||||||
REG_ITSTATE = 9,
|
|
||||||
REG_LR = 10,
|
|
||||||
REG_PC = 11,
|
|
||||||
REG_SP = 12,
|
|
||||||
REG_SPSR = 13,
|
|
||||||
REG_D0 = 14,
|
|
||||||
REG_D1 = 15,
|
|
||||||
REG_D2 = 16,
|
|
||||||
REG_D3 = 17,
|
|
||||||
REG_D4 = 18,
|
|
||||||
REG_D5 = 19,
|
|
||||||
REG_D6 = 20,
|
|
||||||
REG_D7 = 21,
|
|
||||||
REG_D8 = 22,
|
|
||||||
REG_D9 = 23,
|
|
||||||
REG_D10 = 24,
|
|
||||||
REG_D11 = 25,
|
|
||||||
REG_D12 = 26,
|
|
||||||
REG_D13 = 27,
|
|
||||||
REG_D14 = 28,
|
|
||||||
REG_D15 = 29,
|
|
||||||
REG_D16 = 30,
|
|
||||||
REG_D17 = 31,
|
|
||||||
REG_D18 = 32,
|
|
||||||
REG_D19 = 33,
|
|
||||||
REG_D20 = 34,
|
|
||||||
REG_D21 = 35,
|
|
||||||
REG_D22 = 36,
|
|
||||||
REG_D23 = 37,
|
|
||||||
REG_D24 = 38,
|
|
||||||
REG_D25 = 39,
|
|
||||||
REG_D26 = 40,
|
|
||||||
REG_D27 = 41,
|
|
||||||
REG_D28 = 42,
|
|
||||||
REG_D29 = 43,
|
|
||||||
REG_D30 = 44,
|
|
||||||
REG_D31 = 45,
|
|
||||||
REG_FPINST2 = 46,
|
|
||||||
REG_MVFR0 = 47,
|
|
||||||
REG_MVFR1 = 48,
|
|
||||||
REG_MVFR2 = 49,
|
|
||||||
REG_Q0 = 50,
|
|
||||||
REG_Q1 = 51,
|
|
||||||
REG_Q2 = 52,
|
|
||||||
REG_Q3 = 53,
|
|
||||||
REG_Q4 = 54,
|
|
||||||
REG_Q5 = 55,
|
|
||||||
REG_Q6 = 56,
|
|
||||||
REG_Q7 = 57,
|
|
||||||
REG_Q8 = 58,
|
|
||||||
REG_Q9 = 59,
|
|
||||||
REG_Q10 = 60,
|
|
||||||
REG_Q11 = 61,
|
|
||||||
REG_Q12 = 62,
|
|
||||||
REG_Q13 = 63,
|
|
||||||
REG_Q14 = 64,
|
|
||||||
REG_Q15 = 65,
|
|
||||||
REG_R0 = 66,
|
|
||||||
REG_R1 = 67,
|
|
||||||
REG_R2 = 68,
|
|
||||||
REG_R3 = 69,
|
|
||||||
REG_R4 = 70,
|
|
||||||
REG_R5 = 71,
|
|
||||||
REG_R6 = 72,
|
|
||||||
REG_R7 = 73,
|
|
||||||
REG_R8 = 74,
|
|
||||||
REG_R9 = 75,
|
|
||||||
REG_R10 = 76,
|
|
||||||
REG_R11 = 77,
|
|
||||||
REG_R12 = 78,
|
|
||||||
REG_S0 = 79,
|
|
||||||
REG_S1 = 80,
|
|
||||||
REG_S2 = 81,
|
|
||||||
REG_S3 = 82,
|
|
||||||
REG_S4 = 83,
|
|
||||||
REG_S5 = 84,
|
|
||||||
REG_S6 = 85,
|
|
||||||
REG_S7 = 86,
|
|
||||||
REG_S8 = 87,
|
|
||||||
REG_S9 = 88,
|
|
||||||
REG_S10 = 89,
|
|
||||||
REG_S11 = 90,
|
|
||||||
REG_S12 = 91,
|
|
||||||
REG_S13 = 92,
|
|
||||||
REG_S14 = 93,
|
|
||||||
REG_S15 = 94,
|
|
||||||
REG_S16 = 95,
|
|
||||||
REG_S17 = 96,
|
|
||||||
REG_S18 = 97,
|
|
||||||
REG_S19 = 98,
|
|
||||||
REG_S20 = 99,
|
|
||||||
REG_S21 = 100,
|
|
||||||
REG_S22 = 101,
|
|
||||||
REG_S23 = 102,
|
|
||||||
REG_S24 = 103,
|
|
||||||
REG_S25 = 104,
|
|
||||||
REG_S26 = 105,
|
|
||||||
REG_S27 = 106,
|
|
||||||
REG_S28 = 107,
|
|
||||||
REG_S29 = 108,
|
|
||||||
REG_S30 = 109,
|
|
||||||
REG_S31 = 110,
|
|
||||||
REG_C1_C0_2 = 111,
|
|
||||||
REG_C13_C0_2 = 112,
|
|
||||||
REG_C13_C0_3 = 113,
|
|
||||||
REG_IPSR = 114,
|
|
||||||
REG_MSP = 115,
|
|
||||||
REG_PSP = 116,
|
|
||||||
REG_CONTROL = 117,
|
|
||||||
REG_IAPSR = 118,
|
|
||||||
REG_EAPSR = 119,
|
|
||||||
REG_XPSR = 120,
|
|
||||||
REG_EPSR = 121,
|
|
||||||
REG_IEPSR = 122,
|
|
||||||
REG_PRIMASK = 123,
|
|
||||||
REG_BASEPRI = 124,
|
|
||||||
REG_BASEPRI_MAX = 125,
|
|
||||||
REG_FAULTMASK = 126,
|
|
||||||
REG_APSR_NZCVQ = 127,
|
|
||||||
REG_APSR_G = 128,
|
|
||||||
REG_APSR_NZCVQG = 129,
|
|
||||||
REG_IAPSR_NZCVQ = 130,
|
|
||||||
REG_IAPSR_G = 131,
|
|
||||||
REG_IAPSR_NZCVQG = 132,
|
|
||||||
REG_EAPSR_NZCVQ = 133,
|
|
||||||
REG_EAPSR_G = 134,
|
|
||||||
REG_EAPSR_NZCVQG = 135,
|
|
||||||
REG_XPSR_NZCVQ = 136,
|
|
||||||
REG_XPSR_G = 137,
|
|
||||||
REG_XPSR_NZCVQG = 138,
|
|
||||||
REG_CP_REG = 139,
|
|
||||||
REG_ENDING = 140,
|
|
||||||
|
|
||||||
// alias registers
|
|
||||||
REG_R13 = 12,
|
|
||||||
REG_R14 = 10,
|
|
||||||
REG_R15 = 11,
|
|
||||||
REG_SB = 75,
|
|
||||||
REG_SL = 76,
|
|
||||||
REG_FP = 77,
|
|
||||||
REG_IP = 78,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,341 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Arm64
|
|
||||||
{
|
|
||||||
|
|
||||||
// ARM64 CPU
|
|
||||||
|
|
||||||
CPU_ARM64_A57 = 0,
|
|
||||||
CPU_ARM64_A53 = 1,
|
|
||||||
CPU_ARM64_A72 = 2,
|
|
||||||
CPU_ARM64_MAX = 3,
|
|
||||||
CPU_ARM64_ENDING = 4,
|
|
||||||
|
|
||||||
// ARM64 registers
|
|
||||||
|
|
||||||
REG_INVALID = 0,
|
|
||||||
REG_X29 = 1,
|
|
||||||
REG_X30 = 2,
|
|
||||||
REG_NZCV = 3,
|
|
||||||
REG_SP = 4,
|
|
||||||
REG_WSP = 5,
|
|
||||||
REG_WZR = 6,
|
|
||||||
REG_XZR = 7,
|
|
||||||
REG_B0 = 8,
|
|
||||||
REG_B1 = 9,
|
|
||||||
REG_B2 = 10,
|
|
||||||
REG_B3 = 11,
|
|
||||||
REG_B4 = 12,
|
|
||||||
REG_B5 = 13,
|
|
||||||
REG_B6 = 14,
|
|
||||||
REG_B7 = 15,
|
|
||||||
REG_B8 = 16,
|
|
||||||
REG_B9 = 17,
|
|
||||||
REG_B10 = 18,
|
|
||||||
REG_B11 = 19,
|
|
||||||
REG_B12 = 20,
|
|
||||||
REG_B13 = 21,
|
|
||||||
REG_B14 = 22,
|
|
||||||
REG_B15 = 23,
|
|
||||||
REG_B16 = 24,
|
|
||||||
REG_B17 = 25,
|
|
||||||
REG_B18 = 26,
|
|
||||||
REG_B19 = 27,
|
|
||||||
REG_B20 = 28,
|
|
||||||
REG_B21 = 29,
|
|
||||||
REG_B22 = 30,
|
|
||||||
REG_B23 = 31,
|
|
||||||
REG_B24 = 32,
|
|
||||||
REG_B25 = 33,
|
|
||||||
REG_B26 = 34,
|
|
||||||
REG_B27 = 35,
|
|
||||||
REG_B28 = 36,
|
|
||||||
REG_B29 = 37,
|
|
||||||
REG_B30 = 38,
|
|
||||||
REG_B31 = 39,
|
|
||||||
REG_D0 = 40,
|
|
||||||
REG_D1 = 41,
|
|
||||||
REG_D2 = 42,
|
|
||||||
REG_D3 = 43,
|
|
||||||
REG_D4 = 44,
|
|
||||||
REG_D5 = 45,
|
|
||||||
REG_D6 = 46,
|
|
||||||
REG_D7 = 47,
|
|
||||||
REG_D8 = 48,
|
|
||||||
REG_D9 = 49,
|
|
||||||
REG_D10 = 50,
|
|
||||||
REG_D11 = 51,
|
|
||||||
REG_D12 = 52,
|
|
||||||
REG_D13 = 53,
|
|
||||||
REG_D14 = 54,
|
|
||||||
REG_D15 = 55,
|
|
||||||
REG_D16 = 56,
|
|
||||||
REG_D17 = 57,
|
|
||||||
REG_D18 = 58,
|
|
||||||
REG_D19 = 59,
|
|
||||||
REG_D20 = 60,
|
|
||||||
REG_D21 = 61,
|
|
||||||
REG_D22 = 62,
|
|
||||||
REG_D23 = 63,
|
|
||||||
REG_D24 = 64,
|
|
||||||
REG_D25 = 65,
|
|
||||||
REG_D26 = 66,
|
|
||||||
REG_D27 = 67,
|
|
||||||
REG_D28 = 68,
|
|
||||||
REG_D29 = 69,
|
|
||||||
REG_D30 = 70,
|
|
||||||
REG_D31 = 71,
|
|
||||||
REG_H0 = 72,
|
|
||||||
REG_H1 = 73,
|
|
||||||
REG_H2 = 74,
|
|
||||||
REG_H3 = 75,
|
|
||||||
REG_H4 = 76,
|
|
||||||
REG_H5 = 77,
|
|
||||||
REG_H6 = 78,
|
|
||||||
REG_H7 = 79,
|
|
||||||
REG_H8 = 80,
|
|
||||||
REG_H9 = 81,
|
|
||||||
REG_H10 = 82,
|
|
||||||
REG_H11 = 83,
|
|
||||||
REG_H12 = 84,
|
|
||||||
REG_H13 = 85,
|
|
||||||
REG_H14 = 86,
|
|
||||||
REG_H15 = 87,
|
|
||||||
REG_H16 = 88,
|
|
||||||
REG_H17 = 89,
|
|
||||||
REG_H18 = 90,
|
|
||||||
REG_H19 = 91,
|
|
||||||
REG_H20 = 92,
|
|
||||||
REG_H21 = 93,
|
|
||||||
REG_H22 = 94,
|
|
||||||
REG_H23 = 95,
|
|
||||||
REG_H24 = 96,
|
|
||||||
REG_H25 = 97,
|
|
||||||
REG_H26 = 98,
|
|
||||||
REG_H27 = 99,
|
|
||||||
REG_H28 = 100,
|
|
||||||
REG_H29 = 101,
|
|
||||||
REG_H30 = 102,
|
|
||||||
REG_H31 = 103,
|
|
||||||
REG_Q0 = 104,
|
|
||||||
REG_Q1 = 105,
|
|
||||||
REG_Q2 = 106,
|
|
||||||
REG_Q3 = 107,
|
|
||||||
REG_Q4 = 108,
|
|
||||||
REG_Q5 = 109,
|
|
||||||
REG_Q6 = 110,
|
|
||||||
REG_Q7 = 111,
|
|
||||||
REG_Q8 = 112,
|
|
||||||
REG_Q9 = 113,
|
|
||||||
REG_Q10 = 114,
|
|
||||||
REG_Q11 = 115,
|
|
||||||
REG_Q12 = 116,
|
|
||||||
REG_Q13 = 117,
|
|
||||||
REG_Q14 = 118,
|
|
||||||
REG_Q15 = 119,
|
|
||||||
REG_Q16 = 120,
|
|
||||||
REG_Q17 = 121,
|
|
||||||
REG_Q18 = 122,
|
|
||||||
REG_Q19 = 123,
|
|
||||||
REG_Q20 = 124,
|
|
||||||
REG_Q21 = 125,
|
|
||||||
REG_Q22 = 126,
|
|
||||||
REG_Q23 = 127,
|
|
||||||
REG_Q24 = 128,
|
|
||||||
REG_Q25 = 129,
|
|
||||||
REG_Q26 = 130,
|
|
||||||
REG_Q27 = 131,
|
|
||||||
REG_Q28 = 132,
|
|
||||||
REG_Q29 = 133,
|
|
||||||
REG_Q30 = 134,
|
|
||||||
REG_Q31 = 135,
|
|
||||||
REG_S0 = 136,
|
|
||||||
REG_S1 = 137,
|
|
||||||
REG_S2 = 138,
|
|
||||||
REG_S3 = 139,
|
|
||||||
REG_S4 = 140,
|
|
||||||
REG_S5 = 141,
|
|
||||||
REG_S6 = 142,
|
|
||||||
REG_S7 = 143,
|
|
||||||
REG_S8 = 144,
|
|
||||||
REG_S9 = 145,
|
|
||||||
REG_S10 = 146,
|
|
||||||
REG_S11 = 147,
|
|
||||||
REG_S12 = 148,
|
|
||||||
REG_S13 = 149,
|
|
||||||
REG_S14 = 150,
|
|
||||||
REG_S15 = 151,
|
|
||||||
REG_S16 = 152,
|
|
||||||
REG_S17 = 153,
|
|
||||||
REG_S18 = 154,
|
|
||||||
REG_S19 = 155,
|
|
||||||
REG_S20 = 156,
|
|
||||||
REG_S21 = 157,
|
|
||||||
REG_S22 = 158,
|
|
||||||
REG_S23 = 159,
|
|
||||||
REG_S24 = 160,
|
|
||||||
REG_S25 = 161,
|
|
||||||
REG_S26 = 162,
|
|
||||||
REG_S27 = 163,
|
|
||||||
REG_S28 = 164,
|
|
||||||
REG_S29 = 165,
|
|
||||||
REG_S30 = 166,
|
|
||||||
REG_S31 = 167,
|
|
||||||
REG_W0 = 168,
|
|
||||||
REG_W1 = 169,
|
|
||||||
REG_W2 = 170,
|
|
||||||
REG_W3 = 171,
|
|
||||||
REG_W4 = 172,
|
|
||||||
REG_W5 = 173,
|
|
||||||
REG_W6 = 174,
|
|
||||||
REG_W7 = 175,
|
|
||||||
REG_W8 = 176,
|
|
||||||
REG_W9 = 177,
|
|
||||||
REG_W10 = 178,
|
|
||||||
REG_W11 = 179,
|
|
||||||
REG_W12 = 180,
|
|
||||||
REG_W13 = 181,
|
|
||||||
REG_W14 = 182,
|
|
||||||
REG_W15 = 183,
|
|
||||||
REG_W16 = 184,
|
|
||||||
REG_W17 = 185,
|
|
||||||
REG_W18 = 186,
|
|
||||||
REG_W19 = 187,
|
|
||||||
REG_W20 = 188,
|
|
||||||
REG_W21 = 189,
|
|
||||||
REG_W22 = 190,
|
|
||||||
REG_W23 = 191,
|
|
||||||
REG_W24 = 192,
|
|
||||||
REG_W25 = 193,
|
|
||||||
REG_W26 = 194,
|
|
||||||
REG_W27 = 195,
|
|
||||||
REG_W28 = 196,
|
|
||||||
REG_W29 = 197,
|
|
||||||
REG_W30 = 198,
|
|
||||||
REG_X0 = 199,
|
|
||||||
REG_X1 = 200,
|
|
||||||
REG_X2 = 201,
|
|
||||||
REG_X3 = 202,
|
|
||||||
REG_X4 = 203,
|
|
||||||
REG_X5 = 204,
|
|
||||||
REG_X6 = 205,
|
|
||||||
REG_X7 = 206,
|
|
||||||
REG_X8 = 207,
|
|
||||||
REG_X9 = 208,
|
|
||||||
REG_X10 = 209,
|
|
||||||
REG_X11 = 210,
|
|
||||||
REG_X12 = 211,
|
|
||||||
REG_X13 = 212,
|
|
||||||
REG_X14 = 213,
|
|
||||||
REG_X15 = 214,
|
|
||||||
REG_X16 = 215,
|
|
||||||
REG_X17 = 216,
|
|
||||||
REG_X18 = 217,
|
|
||||||
REG_X19 = 218,
|
|
||||||
REG_X20 = 219,
|
|
||||||
REG_X21 = 220,
|
|
||||||
REG_X22 = 221,
|
|
||||||
REG_X23 = 222,
|
|
||||||
REG_X24 = 223,
|
|
||||||
REG_X25 = 224,
|
|
||||||
REG_X26 = 225,
|
|
||||||
REG_X27 = 226,
|
|
||||||
REG_X28 = 227,
|
|
||||||
REG_V0 = 228,
|
|
||||||
REG_V1 = 229,
|
|
||||||
REG_V2 = 230,
|
|
||||||
REG_V3 = 231,
|
|
||||||
REG_V4 = 232,
|
|
||||||
REG_V5 = 233,
|
|
||||||
REG_V6 = 234,
|
|
||||||
REG_V7 = 235,
|
|
||||||
REG_V8 = 236,
|
|
||||||
REG_V9 = 237,
|
|
||||||
REG_V10 = 238,
|
|
||||||
REG_V11 = 239,
|
|
||||||
REG_V12 = 240,
|
|
||||||
REG_V13 = 241,
|
|
||||||
REG_V14 = 242,
|
|
||||||
REG_V15 = 243,
|
|
||||||
REG_V16 = 244,
|
|
||||||
REG_V17 = 245,
|
|
||||||
REG_V18 = 246,
|
|
||||||
REG_V19 = 247,
|
|
||||||
REG_V20 = 248,
|
|
||||||
REG_V21 = 249,
|
|
||||||
REG_V22 = 250,
|
|
||||||
REG_V23 = 251,
|
|
||||||
REG_V24 = 252,
|
|
||||||
REG_V25 = 253,
|
|
||||||
REG_V26 = 254,
|
|
||||||
REG_V27 = 255,
|
|
||||||
REG_V28 = 256,
|
|
||||||
REG_V29 = 257,
|
|
||||||
REG_V30 = 258,
|
|
||||||
REG_V31 = 259,
|
|
||||||
|
|
||||||
// pseudo registers
|
|
||||||
REG_PC = 260,
|
|
||||||
REG_CPACR_EL1 = 261,
|
|
||||||
|
|
||||||
// thread registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_TPIDR_EL0 = 262,
|
|
||||||
REG_TPIDRRO_EL0 = 263,
|
|
||||||
REG_TPIDR_EL1 = 264,
|
|
||||||
REG_PSTATE = 265,
|
|
||||||
|
|
||||||
// exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_ELR_EL0 = 266,
|
|
||||||
REG_ELR_EL1 = 267,
|
|
||||||
REG_ELR_EL2 = 268,
|
|
||||||
REG_ELR_EL3 = 269,
|
|
||||||
|
|
||||||
// stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_SP_EL0 = 270,
|
|
||||||
REG_SP_EL1 = 271,
|
|
||||||
REG_SP_EL2 = 272,
|
|
||||||
REG_SP_EL3 = 273,
|
|
||||||
|
|
||||||
// other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_TTBR0_EL1 = 274,
|
|
||||||
REG_TTBR1_EL1 = 275,
|
|
||||||
REG_ESR_EL0 = 276,
|
|
||||||
REG_ESR_EL1 = 277,
|
|
||||||
REG_ESR_EL2 = 278,
|
|
||||||
REG_ESR_EL3 = 279,
|
|
||||||
REG_FAR_EL0 = 280,
|
|
||||||
REG_FAR_EL1 = 281,
|
|
||||||
REG_FAR_EL2 = 282,
|
|
||||||
REG_FAR_EL3 = 283,
|
|
||||||
REG_PAR_EL1 = 284,
|
|
||||||
REG_MAIR_EL1 = 285,
|
|
||||||
REG_VBAR_EL0 = 286,
|
|
||||||
REG_VBAR_EL1 = 287,
|
|
||||||
REG_VBAR_EL2 = 288,
|
|
||||||
REG_VBAR_EL3 = 289,
|
|
||||||
REG_CP_REG = 290,
|
|
||||||
|
|
||||||
// floating point control and status registers
|
|
||||||
REG_FPCR = 291,
|
|
||||||
REG_FPSR = 292,
|
|
||||||
REG_ENDING = 293,
|
|
||||||
|
|
||||||
// alias registers
|
|
||||||
REG_IP0 = 215,
|
|
||||||
REG_IP1 = 216,
|
|
||||||
REG_FP = 1,
|
|
||||||
REG_LR = 2,
|
|
||||||
|
|
||||||
// ARM64 instructions
|
|
||||||
|
|
||||||
INS_INVALID = 0,
|
|
||||||
INS_MRS = 1,
|
|
||||||
INS_MSR = 2,
|
|
||||||
INS_SYS = 3,
|
|
||||||
INS_SYSL = 4,
|
|
||||||
INS_ENDING = 5,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Common
|
|
||||||
{
|
|
||||||
API_MAJOR = 2,
|
|
||||||
|
|
||||||
API_MINOR = 0,
|
|
||||||
|
|
||||||
API_PATCH = 0,
|
|
||||||
API_EXTRA = 255,
|
|
||||||
VERSION_MAJOR = 2,
|
|
||||||
|
|
||||||
VERSION_MINOR = 0,
|
|
||||||
|
|
||||||
VERSION_PATCH = 0,
|
|
||||||
VERSION_EXTRA = 255,
|
|
||||||
SECOND_SCALE = 1000000,
|
|
||||||
MILISECOND_SCALE = 1000,
|
|
||||||
QUERY_MODE = 1,
|
|
||||||
QUERY_PAGE_SIZE = 2,
|
|
||||||
QUERY_ARCH = 3,
|
|
||||||
QUERY_TIMEOUT = 4,
|
|
||||||
|
|
||||||
CTL_IO_NONE = 0,
|
|
||||||
CTL_IO_WRITE = 1,
|
|
||||||
CTL_IO_READ = 2,
|
|
||||||
CTL_IO_READ_WRITE = 3,
|
|
||||||
|
|
||||||
CTL_UC_MODE = 0,
|
|
||||||
CTL_UC_PAGE_SIZE = 1,
|
|
||||||
CTL_UC_ARCH = 2,
|
|
||||||
CTL_UC_TIMEOUT = 3,
|
|
||||||
CTL_UC_USE_EXITS = 4,
|
|
||||||
CTL_UC_EXITS_CNT = 5,
|
|
||||||
CTL_UC_EXITS = 6,
|
|
||||||
CTL_CPU_MODEL = 7,
|
|
||||||
CTL_TB_REQUEST_CACHE = 8,
|
|
||||||
CTL_TB_REMOVE_CACHE = 9,
|
|
||||||
CTL_TB_FLUSH = 10,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,31 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Error
|
|
||||||
{
|
|
||||||
OK = 0,
|
|
||||||
NOMEM = 1,
|
|
||||||
ARCH = 2,
|
|
||||||
HANDLE = 3,
|
|
||||||
MODE = 4,
|
|
||||||
VERSION = 5,
|
|
||||||
READ_UNMAPPED = 6,
|
|
||||||
WRITE_UNMAPPED = 7,
|
|
||||||
FETCH_UNMAPPED = 8,
|
|
||||||
HOOK = 9,
|
|
||||||
INSN_INVALID = 10,
|
|
||||||
MAP = 11,
|
|
||||||
WRITE_PROT = 12,
|
|
||||||
READ_PROT = 13,
|
|
||||||
FETCH_PROT = 14,
|
|
||||||
ARG = 15,
|
|
||||||
READ_UNALIGNED = 16,
|
|
||||||
WRITE_UNALIGNED = 17,
|
|
||||||
FETCH_UNALIGNED = 18,
|
|
||||||
HOOK_EXIST = 19,
|
|
||||||
RESOURCE = 20,
|
|
||||||
EXCEPTION = 21,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,33 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Hook
|
|
||||||
{
|
|
||||||
INTR = 1,
|
|
||||||
INSN = 2,
|
|
||||||
CODE = 4,
|
|
||||||
BLOCK = 8,
|
|
||||||
MEM_READ_UNMAPPED = 16,
|
|
||||||
MEM_WRITE_UNMAPPED = 32,
|
|
||||||
MEM_FETCH_UNMAPPED = 64,
|
|
||||||
MEM_READ_PROT = 128,
|
|
||||||
MEM_WRITE_PROT = 256,
|
|
||||||
MEM_FETCH_PROT = 512,
|
|
||||||
MEM_READ = 1024,
|
|
||||||
MEM_WRITE = 2048,
|
|
||||||
MEM_FETCH = 4096,
|
|
||||||
MEM_READ_AFTER = 8192,
|
|
||||||
INSN_INVALID = 16384,
|
|
||||||
EDGE_GENERATED = 32768,
|
|
||||||
TCG_OPCODE = 65536,
|
|
||||||
MEM_UNMAPPED = 112,
|
|
||||||
MEM_PROT = 896,
|
|
||||||
MEM_READ_INVALID = 144,
|
|
||||||
MEM_WRITE_INVALID = 288,
|
|
||||||
MEM_FETCH_INVALID = 576,
|
|
||||||
MEM_INVALID = 1008,
|
|
||||||
MEM_VALID = 7168,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,19 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Memory
|
|
||||||
{
|
|
||||||
READ = 16,
|
|
||||||
WRITE = 17,
|
|
||||||
FETCH = 18,
|
|
||||||
READ_UNMAPPED = 19,
|
|
||||||
WRITE_UNMAPPED = 20,
|
|
||||||
FETCH_UNMAPPED = 21,
|
|
||||||
WRITE_PROT = 22,
|
|
||||||
READ_PROT = 23,
|
|
||||||
FETCH_PROT = 24,
|
|
||||||
READ_AFTER = 25,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,35 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Mode
|
|
||||||
{
|
|
||||||
LITTLE_ENDIAN = 0,
|
|
||||||
BIG_ENDIAN = 1073741824,
|
|
||||||
ARM = 0,
|
|
||||||
THUMB = 16,
|
|
||||||
MCLASS = 32,
|
|
||||||
V8 = 64,
|
|
||||||
ARMBE8 = 1024,
|
|
||||||
ARM926 = 128,
|
|
||||||
ARM946 = 256,
|
|
||||||
ARM1176 = 512,
|
|
||||||
MICRO = 16,
|
|
||||||
MIPS3 = 32,
|
|
||||||
MIPS32R6 = 64,
|
|
||||||
MIPS32 = 4,
|
|
||||||
MIPS64 = 8,
|
|
||||||
MODE_16 = 2,
|
|
||||||
MODE_32 = 4,
|
|
||||||
MODE_64 = 8,
|
|
||||||
PPC32 = 4,
|
|
||||||
PPC64 = 8,
|
|
||||||
QPX = 16,
|
|
||||||
SPARC32 = 4,
|
|
||||||
SPARC64 = 8,
|
|
||||||
V9 = 16,
|
|
||||||
RISCV32 = 4,
|
|
||||||
RISCV64 = 8,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Permission
|
|
||||||
{
|
|
||||||
NONE = 0,
|
|
||||||
READ = 1,
|
|
||||||
WRITE = 2,
|
|
||||||
EXEC = 4,
|
|
||||||
ALL = 7,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum TCG
|
|
||||||
{
|
|
||||||
OP_SUB = 0,
|
|
||||||
OP_FLAG_CMP = 1,
|
|
||||||
OP_FLAG_DIRECT = 2,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,101 +0,0 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native.Const;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
public static partial class Interface
|
|
||||||
{
|
|
||||||
public static bool IsUnicornAvailable { get; private set; } = true;
|
|
||||||
|
|
||||||
private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
|
||||||
{
|
|
||||||
if (libraryName == "unicorn")
|
|
||||||
{
|
|
||||||
string loadPath = $"{Path.GetDirectoryName(assembly.Location)}/";
|
|
||||||
loadPath += OperatingSystem.IsWindows() ? $"{libraryName}.dll" : $"lib{libraryName}.so";
|
|
||||||
|
|
||||||
if (!NativeLibrary.TryLoad(loadPath, out IntPtr libraryPtr))
|
|
||||||
{
|
|
||||||
IsUnicornAvailable = false;
|
|
||||||
Console.Error.WriteLine($"ERROR: Could not find unicorn at: {loadPath}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return libraryPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, fallback to default import resolver.
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Interface()
|
|
||||||
{
|
|
||||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), ImportResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Checked(Error error)
|
|
||||||
{
|
|
||||||
if (error != Error.OK)
|
|
||||||
{
|
|
||||||
throw new UnicornException(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MarshalArrayOf<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr input, int length, out T[] output)
|
|
||||||
{
|
|
||||||
int size = Marshal.SizeOf<T>();
|
|
||||||
|
|
||||||
output = new T[length];
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
IntPtr item = new IntPtr(input.ToInt64() + i * size);
|
|
||||||
|
|
||||||
output[i] = Marshal.PtrToStructure<T>(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial uint uc_version(out uint major, out uint minor);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_open(Arch arch, Mode mode, out IntPtr uc);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_close(IntPtr uc);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial IntPtr uc_strerror(Error err);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_reg_write(IntPtr uc, int regid, byte[] value);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_reg_read(IntPtr uc, int regid, byte[] value);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_unmap(IntPtr uc, ulong address, ulong size);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct UnicornMemoryRegion
|
|
||||||
{
|
|
||||||
public UInt64 begin; // begin address of the region (inclusive)
|
|
||||||
public UInt64 end; // end address of the region (inclusive)
|
|
||||||
public UInt32 perms; // memory permissions of the region
|
|
||||||
}
|
|
||||||
}
|
|
@@ -10,4 +10,8 @@
|
|||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="UnicornEngine.Unicorn" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -1,62 +1,45 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native;
|
using System;
|
||||||
using Ryujinx.Tests.Unicorn.Native.Const;
|
using UnicornEngine.Const;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
namespace Ryujinx.Tests.Unicorn
|
||||||
{
|
{
|
||||||
public class UnicornAArch32 : IDisposable
|
public class UnicornAArch32 : IDisposable
|
||||||
{
|
{
|
||||||
internal readonly IntPtr uc;
|
internal readonly UnicornEngine.Unicorn uc;
|
||||||
private bool _isDisposed = false;
|
private bool _isDisposed;
|
||||||
|
|
||||||
public IndexedProperty<int, uint> R
|
public IndexedProperty<int, uint> R => new(GetX, SetX);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new IndexedProperty<int, uint>(
|
|
||||||
(int i) => GetX(i),
|
|
||||||
(int i, uint value) => SetX(i, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexedProperty<int, SimdValue> Q
|
public IndexedProperty<int, SimdValue> Q => new(GetQ, SetQ);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new IndexedProperty<int, SimdValue>(
|
|
||||||
(int i) => GetQ(i),
|
|
||||||
(int i, SimdValue value) => SetQ(i, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint LR
|
public uint LR
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm.REG_LR);
|
get => GetRegister(Arm.UC_ARM_REG_LR);
|
||||||
set => SetRegister(Arm.REG_LR, value);
|
set => SetRegister(Arm.UC_ARM_REG_LR, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint SP
|
public uint SP
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm.REG_SP);
|
get => GetRegister(Arm.UC_ARM_REG_SP);
|
||||||
set => SetRegister(Arm.REG_SP, value);
|
set => SetRegister(Arm.UC_ARM_REG_SP, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint PC
|
public uint PC
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm.REG_PC) & 0xfffffffeu;
|
get => GetRegister(Arm.UC_ARM_REG_PC) & 0xfffffffeu;
|
||||||
set => SetRegister(Arm.REG_PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u));
|
set => SetRegister(Arm.UC_ARM_REG_PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u));
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint CPSR
|
public uint CPSR
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm.REG_CPSR);
|
get => GetRegister(Arm.UC_ARM_REG_CPSR);
|
||||||
set => SetRegister(Arm.REG_CPSR, value);
|
set => SetRegister(Arm.UC_ARM_REG_CPSR, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpscr
|
public int Fpscr
|
||||||
{
|
{
|
||||||
get => (int)GetRegister(Arm.REG_FPSCR) | ((int)GetRegister(Arm.REG_FPSCR_NZCV));
|
get => (int)GetRegister(Arm.UC_ARM_REG_FPSCR) | ((int)GetRegister(Arm.UC_ARM_REG_FPSCR_NZCV));
|
||||||
set => SetRegister(Arm.REG_FPSCR, (uint)value);
|
set => SetRegister(Arm.UC_ARM_REG_FPSCR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool QFlag
|
public bool QFlag
|
||||||
@@ -95,16 +78,16 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u);
|
CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u);
|
||||||
SetRegister(Arm.REG_PC, (GetRegister(Arm.REG_PC) & 0xfffffffeu) | (value ? 1u : 0u));
|
SetRegister(Arm.UC_ARM_REG_PC, (GetRegister(Arm.UC_ARM_REG_PC) & 0xfffffffeu) | (value ? 1u : 0u));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnicornAArch32()
|
public UnicornAArch32()
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_open(Arch.ARM, Mode.LITTLE_ENDIAN, out uc));
|
uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM, Common.UC_MODE_LITTLE_ENDIAN);
|
||||||
|
|
||||||
SetRegister(Arm.REG_C1_C0_2, GetRegister(Arm.REG_C1_C0_2) | 0xf00000);
|
SetRegister(Arm.UC_ARM_REG_C1_C0_2, GetRegister(Arm.UC_ARM_REG_C1_C0_2) | 0xf00000);
|
||||||
SetRegister(Arm.REG_FPEXC, 0x40000000);
|
SetRegister(Arm.UC_ARM_REG_FPEXC, 0x40000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
~UnicornAArch32()
|
~UnicornAArch32()
|
||||||
@@ -122,14 +105,15 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
{
|
{
|
||||||
if (!_isDisposed)
|
if (!_isDisposed)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_close(uc));
|
uc.Close();
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunForCount(ulong count)
|
public void RunForCount(ulong count)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
|
// FIXME: untilAddr should be 0xFFFFFFFFFFFFFFFFu
|
||||||
|
uc.EmuStart(this.PC, -1, 0, (long)count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Step()
|
public void Step()
|
||||||
@@ -137,44 +121,44 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
RunForCount(1);
|
RunForCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Arm[] XRegisters = new Arm[16]
|
private static int[] XRegisters =
|
||||||
{
|
{
|
||||||
Arm.REG_R0,
|
Arm.UC_ARM_REG_R0,
|
||||||
Arm.REG_R1,
|
Arm.UC_ARM_REG_R1,
|
||||||
Arm.REG_R2,
|
Arm.UC_ARM_REG_R2,
|
||||||
Arm.REG_R3,
|
Arm.UC_ARM_REG_R3,
|
||||||
Arm.REG_R4,
|
Arm.UC_ARM_REG_R4,
|
||||||
Arm.REG_R5,
|
Arm.UC_ARM_REG_R5,
|
||||||
Arm.REG_R6,
|
Arm.UC_ARM_REG_R6,
|
||||||
Arm.REG_R7,
|
Arm.UC_ARM_REG_R7,
|
||||||
Arm.REG_R8,
|
Arm.UC_ARM_REG_R8,
|
||||||
Arm.REG_R9,
|
Arm.UC_ARM_REG_R9,
|
||||||
Arm.REG_R10,
|
Arm.UC_ARM_REG_R10,
|
||||||
Arm.REG_R11,
|
Arm.UC_ARM_REG_R11,
|
||||||
Arm.REG_R12,
|
Arm.UC_ARM_REG_R12,
|
||||||
Arm.REG_R13,
|
Arm.UC_ARM_REG_R13,
|
||||||
Arm.REG_R14,
|
Arm.UC_ARM_REG_R14,
|
||||||
Arm.REG_R15,
|
Arm.UC_ARM_REG_R15,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Arm[] QRegisters = new Arm[16]
|
private static int[] QRegisters =
|
||||||
{
|
{
|
||||||
Arm.REG_Q0,
|
Arm.UC_ARM_REG_Q0,
|
||||||
Arm.REG_Q1,
|
Arm.UC_ARM_REG_Q1,
|
||||||
Arm.REG_Q2,
|
Arm.UC_ARM_REG_Q2,
|
||||||
Arm.REG_Q3,
|
Arm.UC_ARM_REG_Q3,
|
||||||
Arm.REG_Q4,
|
Arm.UC_ARM_REG_Q4,
|
||||||
Arm.REG_Q5,
|
Arm.UC_ARM_REG_Q5,
|
||||||
Arm.REG_Q6,
|
Arm.UC_ARM_REG_Q6,
|
||||||
Arm.REG_Q7,
|
Arm.UC_ARM_REG_Q7,
|
||||||
Arm.REG_Q8,
|
Arm.UC_ARM_REG_Q8,
|
||||||
Arm.REG_Q9,
|
Arm.UC_ARM_REG_Q9,
|
||||||
Arm.REG_Q10,
|
Arm.UC_ARM_REG_Q10,
|
||||||
Arm.REG_Q11,
|
Arm.UC_ARM_REG_Q11,
|
||||||
Arm.REG_Q12,
|
Arm.UC_ARM_REG_Q12,
|
||||||
Arm.REG_Q13,
|
Arm.UC_ARM_REG_Q13,
|
||||||
Arm.REG_Q14,
|
Arm.UC_ARM_REG_Q14,
|
||||||
Arm.REG_Q15
|
Arm.UC_ARM_REG_Q15
|
||||||
};
|
};
|
||||||
|
|
||||||
public uint GetX(int index)
|
public uint GetX(int index)
|
||||||
@@ -205,7 +189,7 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead.
|
// Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead.
|
||||||
return GetVector((Arm)((int)Arm.REG_D0 + index * 2));
|
return GetVector(Arm.UC_ARM_REG_D0 + index * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetQ(int index, SimdValue value)
|
public void SetQ(int index, SimdValue value)
|
||||||
@@ -215,96 +199,85 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetVector((Arm)((int)Arm.REG_D0 + index * 2), value);
|
SetVector(Arm.UC_ARM_REG_D0 + index * 2, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetRegister(Arm register)
|
public uint GetRegister(int register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[4];
|
byte[] data = new byte[4];
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_read(uc, (int)register, data));
|
uc.RegRead(register, data);
|
||||||
|
|
||||||
return (uint)BitConverter.ToInt32(data, 0);
|
return BitConverter.ToUInt32(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRegister(Arm register, uint value)
|
public void SetRegister(int register, uint value)
|
||||||
{
|
{
|
||||||
byte[] data = BitConverter.GetBytes(value);
|
byte[] data = BitConverter.GetBytes(value);
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
uc.RegWrite(register, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimdValue GetVector(Arm register)
|
public SimdValue GetVector(int register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[8];
|
byte[] data = new byte[8];
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_read(uc, (int)register, data));
|
uc.RegRead(register, data);
|
||||||
ulong lo = BitConverter.ToUInt64(data, 0);
|
ulong lo = BitConverter.ToUInt64(data, 0);
|
||||||
Interface.Checked(Interface.uc_reg_read(uc, (int)register + 1, data));
|
uc.RegRead(register + 1, data);
|
||||||
ulong hi = BitConverter.ToUInt64(data, 0);
|
ulong hi = BitConverter.ToUInt64(data, 0);
|
||||||
|
|
||||||
return new SimdValue(lo, hi);
|
return new SimdValue(lo, hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVector(Arm register, SimdValue value)
|
private void SetVector(int register, SimdValue value)
|
||||||
{
|
{
|
||||||
byte[] data = BitConverter.GetBytes(value.GetUInt64(0));
|
byte[] data = BitConverter.GetBytes(value.GetUInt64(0));
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
uc.RegWrite(register, data);
|
||||||
data = BitConverter.GetBytes(value.GetUInt64(1));
|
data = BitConverter.GetBytes(value.GetUInt64(1));
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register + 1, data));
|
uc.RegWrite(register + 1, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] MemoryRead(ulong address, ulong size)
|
public byte[] MemoryRead(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
byte[] value = new byte[size];
|
byte[] value = new byte[size];
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_mem_read(uc, address, value, size));
|
uc.MemRead((long)address, value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0];
|
public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0];
|
||||||
public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0);
|
public ushort MemoryRead16(ulong address) => BitConverter.ToUInt16(MemoryRead(address, 2), 0);
|
||||||
public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0);
|
public uint MemoryRead32(ulong address) => BitConverter.ToUInt32(MemoryRead(address, 4), 0);
|
||||||
public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0);
|
public ulong MemoryRead64(ulong address) => BitConverter.ToUInt64(MemoryRead(address, 8), 0);
|
||||||
|
|
||||||
public void MemoryWrite(ulong address, byte[] value)
|
public void MemoryWrite(ulong address, byte[] value)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
|
uc.MemWrite((long)address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new byte[] { value });
|
public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new[] { value });
|
||||||
public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite16(ulong address, short value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite16(ulong address, ushort value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite32(ulong address, int value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite32(ulong address, uint value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite64(ulong address, long value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite64(ulong address, ulong value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
|
|
||||||
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
|
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions));
|
uc.MemMap((long)address, (long)size, (int)permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MemoryUnmap(ulong address, ulong size)
|
public void MemoryUnmap(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_unmap(uc, address, size));
|
uc.MemUnmap((long)address, (long)size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
|
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions));
|
uc.MemProtect((long)address, (long)size, (int)permissions);
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsAvailable()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Interface.uc_version(out _, out _);
|
|
||||||
}
|
|
||||||
catch (DllNotFoundException) { }
|
|
||||||
|
|
||||||
return Interface.IsUnicornAvailable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,68 +1,51 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native;
|
|
||||||
using Ryujinx.Tests.Unicorn.Native.Const;
|
|
||||||
using System;
|
using System;
|
||||||
|
using UnicornEngine.Const;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
namespace Ryujinx.Tests.Unicorn
|
||||||
{
|
{
|
||||||
public class UnicornAArch64 : IDisposable
|
public class UnicornAArch64 : IDisposable
|
||||||
{
|
{
|
||||||
internal readonly IntPtr uc;
|
internal readonly UnicornEngine.Unicorn uc;
|
||||||
private bool _isDisposed = false;
|
private bool _isDisposed;
|
||||||
|
|
||||||
public IndexedProperty<int, ulong> X
|
public IndexedProperty<int, ulong> X => new(GetX, SetX);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new IndexedProperty<int, ulong>(
|
|
||||||
(int i) => GetX(i),
|
|
||||||
(int i, ulong value) => SetX(i, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexedProperty<int, SimdValue> Q
|
public IndexedProperty<int, SimdValue> Q => new(GetQ, SetQ);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new IndexedProperty<int, SimdValue>(
|
|
||||||
(int i) => GetQ(i),
|
|
||||||
(int i, SimdValue value) => SetQ(i, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong LR
|
public ulong LR
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm64.REG_LR);
|
get => GetRegister(Arm64.UC_ARM64_REG_LR);
|
||||||
set => SetRegister(Arm64.REG_LR, value);
|
set => SetRegister(Arm64.UC_ARM64_REG_LR, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong SP
|
public ulong SP
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm64.REG_SP);
|
get => GetRegister(Arm64.UC_ARM64_REG_SP);
|
||||||
set => SetRegister(Arm64.REG_SP, value);
|
set => SetRegister(Arm64.UC_ARM64_REG_SP, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong PC
|
public ulong PC
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm64.REG_PC);
|
get => GetRegister(Arm64.UC_ARM64_REG_PC);
|
||||||
set => SetRegister(Arm64.REG_PC, value);
|
set => SetRegister(Arm64.UC_ARM64_REG_PC, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Pstate
|
public uint Pstate
|
||||||
{
|
{
|
||||||
get => (uint)GetRegister(Arm64.REG_PSTATE);
|
get => (uint)GetRegister(Arm64.UC_ARM64_REG_PSTATE);
|
||||||
set => SetRegister(Arm64.REG_PSTATE, (uint)value);
|
set => SetRegister(Arm64.UC_ARM64_REG_PSTATE, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpcr
|
public int Fpcr
|
||||||
{
|
{
|
||||||
get => (int)GetRegister(Arm64.REG_FPCR);
|
get => (int)GetRegister(Arm64.UC_ARM64_REG_FPCR);
|
||||||
set => SetRegister(Arm64.REG_FPCR, (uint)value);
|
set => SetRegister(Arm64.UC_ARM64_REG_FPCR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpsr
|
public int Fpsr
|
||||||
{
|
{
|
||||||
get => (int)GetRegister(Arm64.REG_FPSR);
|
get => (int)GetRegister(Arm64.UC_ARM64_REG_FPSR);
|
||||||
set => SetRegister(Arm64.REG_FPSR, (uint)value);
|
set => SetRegister(Arm64.UC_ARM64_REG_FPSR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OverflowFlag
|
public bool OverflowFlag
|
||||||
@@ -91,9 +74,9 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
|
|
||||||
public UnicornAArch64()
|
public UnicornAArch64()
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_open(Arch.ARM64, Mode.LITTLE_ENDIAN, out uc));
|
uc = new UnicornEngine.Unicorn(Common.UC_ARCH_ARM64, Common.UC_MODE_LITTLE_ENDIAN);
|
||||||
|
|
||||||
SetRegister(Arm64.REG_CPACR_EL1, 0x00300000);
|
SetRegister(Arm64.UC_ARM64_REG_CPACR_EL1, 0x00300000);
|
||||||
}
|
}
|
||||||
|
|
||||||
~UnicornAArch64()
|
~UnicornAArch64()
|
||||||
@@ -111,14 +94,15 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
{
|
{
|
||||||
if (!_isDisposed)
|
if (!_isDisposed)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_close(uc));
|
uc.Close();
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunForCount(ulong count)
|
public void RunForCount(ulong count)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
|
// FIXME: untilAddr should be 0xFFFFFFFFFFFFFFFFul
|
||||||
|
uc.EmuStart((long)this.PC, -1, 0, (long)count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Step()
|
public void Step()
|
||||||
@@ -126,75 +110,75 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
RunForCount(1);
|
RunForCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Arm64[] XRegisters = new Arm64[31]
|
private static int[] XRegisters =
|
||||||
{
|
{
|
||||||
Arm64.REG_X0,
|
Arm64.UC_ARM64_REG_X0,
|
||||||
Arm64.REG_X1,
|
Arm64.UC_ARM64_REG_X1,
|
||||||
Arm64.REG_X2,
|
Arm64.UC_ARM64_REG_X2,
|
||||||
Arm64.REG_X3,
|
Arm64.UC_ARM64_REG_X3,
|
||||||
Arm64.REG_X4,
|
Arm64.UC_ARM64_REG_X4,
|
||||||
Arm64.REG_X5,
|
Arm64.UC_ARM64_REG_X5,
|
||||||
Arm64.REG_X6,
|
Arm64.UC_ARM64_REG_X6,
|
||||||
Arm64.REG_X7,
|
Arm64.UC_ARM64_REG_X7,
|
||||||
Arm64.REG_X8,
|
Arm64.UC_ARM64_REG_X8,
|
||||||
Arm64.REG_X9,
|
Arm64.UC_ARM64_REG_X9,
|
||||||
Arm64.REG_X10,
|
Arm64.UC_ARM64_REG_X10,
|
||||||
Arm64.REG_X11,
|
Arm64.UC_ARM64_REG_X11,
|
||||||
Arm64.REG_X12,
|
Arm64.UC_ARM64_REG_X12,
|
||||||
Arm64.REG_X13,
|
Arm64.UC_ARM64_REG_X13,
|
||||||
Arm64.REG_X14,
|
Arm64.UC_ARM64_REG_X14,
|
||||||
Arm64.REG_X15,
|
Arm64.UC_ARM64_REG_X15,
|
||||||
Arm64.REG_X16,
|
Arm64.UC_ARM64_REG_X16,
|
||||||
Arm64.REG_X17,
|
Arm64.UC_ARM64_REG_X17,
|
||||||
Arm64.REG_X18,
|
Arm64.UC_ARM64_REG_X18,
|
||||||
Arm64.REG_X19,
|
Arm64.UC_ARM64_REG_X19,
|
||||||
Arm64.REG_X20,
|
Arm64.UC_ARM64_REG_X20,
|
||||||
Arm64.REG_X21,
|
Arm64.UC_ARM64_REG_X21,
|
||||||
Arm64.REG_X22,
|
Arm64.UC_ARM64_REG_X22,
|
||||||
Arm64.REG_X23,
|
Arm64.UC_ARM64_REG_X23,
|
||||||
Arm64.REG_X24,
|
Arm64.UC_ARM64_REG_X24,
|
||||||
Arm64.REG_X25,
|
Arm64.UC_ARM64_REG_X25,
|
||||||
Arm64.REG_X26,
|
Arm64.UC_ARM64_REG_X26,
|
||||||
Arm64.REG_X27,
|
Arm64.UC_ARM64_REG_X27,
|
||||||
Arm64.REG_X28,
|
Arm64.UC_ARM64_REG_X28,
|
||||||
Arm64.REG_X29,
|
Arm64.UC_ARM64_REG_X29,
|
||||||
Arm64.REG_X30,
|
Arm64.UC_ARM64_REG_X30,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Arm64[] QRegisters = new Arm64[32]
|
private static int[] QRegisters =
|
||||||
{
|
{
|
||||||
Arm64.REG_Q0,
|
Arm64.UC_ARM64_REG_Q0,
|
||||||
Arm64.REG_Q1,
|
Arm64.UC_ARM64_REG_Q1,
|
||||||
Arm64.REG_Q2,
|
Arm64.UC_ARM64_REG_Q2,
|
||||||
Arm64.REG_Q3,
|
Arm64.UC_ARM64_REG_Q3,
|
||||||
Arm64.REG_Q4,
|
Arm64.UC_ARM64_REG_Q4,
|
||||||
Arm64.REG_Q5,
|
Arm64.UC_ARM64_REG_Q5,
|
||||||
Arm64.REG_Q6,
|
Arm64.UC_ARM64_REG_Q6,
|
||||||
Arm64.REG_Q7,
|
Arm64.UC_ARM64_REG_Q7,
|
||||||
Arm64.REG_Q8,
|
Arm64.UC_ARM64_REG_Q8,
|
||||||
Arm64.REG_Q9,
|
Arm64.UC_ARM64_REG_Q9,
|
||||||
Arm64.REG_Q10,
|
Arm64.UC_ARM64_REG_Q10,
|
||||||
Arm64.REG_Q11,
|
Arm64.UC_ARM64_REG_Q11,
|
||||||
Arm64.REG_Q12,
|
Arm64.UC_ARM64_REG_Q12,
|
||||||
Arm64.REG_Q13,
|
Arm64.UC_ARM64_REG_Q13,
|
||||||
Arm64.REG_Q14,
|
Arm64.UC_ARM64_REG_Q14,
|
||||||
Arm64.REG_Q15,
|
Arm64.UC_ARM64_REG_Q15,
|
||||||
Arm64.REG_Q16,
|
Arm64.UC_ARM64_REG_Q16,
|
||||||
Arm64.REG_Q17,
|
Arm64.UC_ARM64_REG_Q17,
|
||||||
Arm64.REG_Q18,
|
Arm64.UC_ARM64_REG_Q18,
|
||||||
Arm64.REG_Q19,
|
Arm64.UC_ARM64_REG_Q19,
|
||||||
Arm64.REG_Q20,
|
Arm64.UC_ARM64_REG_Q20,
|
||||||
Arm64.REG_Q21,
|
Arm64.UC_ARM64_REG_Q21,
|
||||||
Arm64.REG_Q22,
|
Arm64.UC_ARM64_REG_Q22,
|
||||||
Arm64.REG_Q23,
|
Arm64.UC_ARM64_REG_Q23,
|
||||||
Arm64.REG_Q24,
|
Arm64.UC_ARM64_REG_Q24,
|
||||||
Arm64.REG_Q25,
|
Arm64.UC_ARM64_REG_Q25,
|
||||||
Arm64.REG_Q26,
|
Arm64.UC_ARM64_REG_Q26,
|
||||||
Arm64.REG_Q27,
|
Arm64.UC_ARM64_REG_Q27,
|
||||||
Arm64.REG_Q28,
|
Arm64.UC_ARM64_REG_Q28,
|
||||||
Arm64.REG_Q29,
|
Arm64.UC_ARM64_REG_Q29,
|
||||||
Arm64.REG_Q30,
|
Arm64.UC_ARM64_REG_Q30,
|
||||||
Arm64.REG_Q31,
|
Arm64.UC_ARM64_REG_Q31,
|
||||||
};
|
};
|
||||||
|
|
||||||
public ulong GetX(int index)
|
public ulong GetX(int index)
|
||||||
@@ -237,89 +221,78 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
SetVector(QRegisters[index], value);
|
SetVector(QRegisters[index], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong GetRegister(Arm64 register)
|
private ulong GetRegister(int register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[8];
|
byte[] data = new byte[8];
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_read(uc, (int)register, data));
|
uc.RegRead(register, data);
|
||||||
|
|
||||||
return (ulong)BitConverter.ToInt64(data, 0);
|
return BitConverter.ToUInt64(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetRegister(Arm64 register, ulong value)
|
private void SetRegister(int register, ulong value)
|
||||||
{
|
{
|
||||||
byte[] data = BitConverter.GetBytes(value);
|
byte[] data = BitConverter.GetBytes(value);
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
uc.RegWrite(register, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimdValue GetVector(Arm64 register)
|
private SimdValue GetVector(int register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[16];
|
byte[] data = new byte[16];
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_read(uc, (int)register, data));
|
uc.RegRead(register, data);
|
||||||
|
|
||||||
return new SimdValue(data);
|
return new SimdValue(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVector(Arm64 register, SimdValue value)
|
private void SetVector(int register, SimdValue value)
|
||||||
{
|
{
|
||||||
byte[] data = value.ToArray();
|
byte[] data = value.ToArray();
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
uc.RegWrite(register, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] MemoryRead(ulong address, ulong size)
|
public byte[] MemoryRead(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
byte[] value = new byte[size];
|
byte[] value = new byte[size];
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_mem_read(uc, address, value, size));
|
uc.MemRead((long)address, value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte MemoryRead8 (ulong address) => MemoryRead(address, 1)[0];
|
public byte MemoryRead8 (ulong address) => MemoryRead(address, 1)[0];
|
||||||
public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0);
|
public ushort MemoryRead16(ulong address) => BitConverter.ToUInt16(MemoryRead(address, 2), 0);
|
||||||
public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0);
|
public uint MemoryRead32(ulong address) => BitConverter.ToUInt32(MemoryRead(address, 4), 0);
|
||||||
public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0);
|
public ulong MemoryRead64(ulong address) => BitConverter.ToUInt64(MemoryRead(address, 8), 0);
|
||||||
|
|
||||||
public void MemoryWrite(ulong address, byte[] value)
|
public void MemoryWrite(ulong address, byte[] value)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
|
uc.MemWrite((long)address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MemoryWrite8 (ulong address, byte value) => MemoryWrite(address, new byte[]{value});
|
public void MemoryWrite8 (ulong address, byte value) => MemoryWrite(address, new[]{ value });
|
||||||
public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite16(ulong address, short value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite16(ulong address, ushort value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite32(ulong address, int value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite32(ulong address, uint value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite64(ulong address, long value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
public void MemoryWrite64(ulong address, ulong value) => MemoryWrite(address, BitConverter.GetBytes(value));
|
||||||
|
|
||||||
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
|
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions));
|
uc.MemMap((long)address, (long)size, (int)permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MemoryUnmap(ulong address, ulong size)
|
public void MemoryUnmap(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_unmap(uc, address, size));
|
uc.MemUnmap((long)address, (long)size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
|
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions));
|
uc.MemProtect((long)address, (long)size, (int)permissions);
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsAvailable()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Interface.uc_version(out _, out _);
|
|
||||||
}
|
|
||||||
catch (DllNotFoundException) { }
|
|
||||||
|
|
||||||
return Interface.IsUnicornAvailable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,24 +0,0 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native.Const;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
|
||||||
{
|
|
||||||
public class UnicornException : Exception
|
|
||||||
{
|
|
||||||
public readonly Error Error;
|
|
||||||
|
|
||||||
internal UnicornException(Error error)
|
|
||||||
{
|
|
||||||
Error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Message
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringAnsi(Native.Interface.uc_strerror(Error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
# Unicorn
|
|
||||||
|
|
||||||
Unicorn is a CPU simulator with bindings in many languages, including
|
|
||||||
C#/.NET.
|
|
||||||
It is used by the Ryujinx test suite for comparative testing with its built-in
|
|
||||||
CPU simulator, Armeilleure.
|
|
||||||
|
|
||||||
## Windows
|
|
||||||
|
|
||||||
On Windows, Unicorn is shipped as a pre-compiled dynamic library (`.dll`), licenced under the GPLv2.
|
|
||||||
|
|
||||||
The source code for `windows/unicorn.dll` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698
|
|
||||||
|
|
||||||
## Linux
|
|
||||||
|
|
||||||
On Windows, Unicorn is shipped as a pre-compiled shared object (`.so`), licenced under the GPLv2.
|
|
||||||
|
|
||||||
The source code for `linux/unicorn.so` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698
|
|
||||||
|
|
||||||
See https://github.com/Ryujinx/Ryujinx/pull/1433 for details.
|
|
Binary file not shown.
Binary file not shown.
@@ -1,199 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# Unicorn Engine
|
|
||||||
# By Dang Hoang Vu, 2013
|
|
||||||
# Modified for Ryujinx from: https://github.com/unicorn-engine/unicorn/blob/6c1cbef6ac505d355033aef1176b684d02e1eb3a/bindings/const_generator.py
|
|
||||||
from __future__ import print_function
|
|
||||||
import sys, re, os
|
|
||||||
|
|
||||||
include = [ 'arm.h', 'arm64.h', 'unicorn.h' ]
|
|
||||||
split_common = [ 'ARCH', 'MODE', 'ERR', 'MEM', 'TCG', 'HOOK', 'PROT' ]
|
|
||||||
|
|
||||||
template = {
|
|
||||||
'dotnet': {
|
|
||||||
'header': "// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT\n\n// ReSharper disable InconsistentNaming\nnamespace Ryujinx.Tests.Unicorn.Native.Const\n{\n public enum %s\n {\n",
|
|
||||||
'footer': " }\n}\n",
|
|
||||||
'line_format': ' %s = %s,\n',
|
|
||||||
'out_file': os.path.join(os.path.dirname(__file__), 'Native', 'Const', '%s.cs'),
|
|
||||||
# prefixes for constant filenames of all archs - case sensitive
|
|
||||||
'arm.h': 'Arm',
|
|
||||||
'arm64.h': 'Arm64',
|
|
||||||
'unicorn.h': 'Common',
|
|
||||||
# prefixes for filenames of split_common values - case sensitive
|
|
||||||
'ARCH': 'Arch',
|
|
||||||
'MODE': 'Mode',
|
|
||||||
'ERR': 'Error',
|
|
||||||
'MEM': 'Memory',
|
|
||||||
'TCG': 'TCG',
|
|
||||||
'HOOK': 'Hook',
|
|
||||||
'PROT': 'Permission',
|
|
||||||
'comment_open': ' //',
|
|
||||||
'comment_close': '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# markup for comments to be added to autogen files
|
|
||||||
MARKUP = '//>'
|
|
||||||
|
|
||||||
def gen(unicorn_repo_path):
|
|
||||||
global include
|
|
||||||
include_dir = os.path.join(unicorn_repo_path, 'include', 'unicorn')
|
|
||||||
templ = template["dotnet"]
|
|
||||||
for target in include:
|
|
||||||
prefix = templ[target]
|
|
||||||
outfile = open(templ['out_file'] %(prefix), 'wb') # open as binary prevents windows newlines
|
|
||||||
outfile.write((templ['header'] % (prefix)).encode("utf-8"))
|
|
||||||
if target == 'unicorn.h':
|
|
||||||
prefix = ''
|
|
||||||
for cat in split_common:
|
|
||||||
with open(templ['out_file'] %(templ[cat]), 'wb') as file:
|
|
||||||
file.write((templ['header'] %(templ[cat])).encode("utf-8"))
|
|
||||||
with open(os.path.join(include_dir, target)) as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
|
|
||||||
previous = {}
|
|
||||||
count = 0
|
|
||||||
skip = 0
|
|
||||||
in_comment = False
|
|
||||||
|
|
||||||
for lno, line in enumerate(lines):
|
|
||||||
if "/*" in line:
|
|
||||||
in_comment = True
|
|
||||||
if "*/" in line:
|
|
||||||
in_comment = False
|
|
||||||
if in_comment:
|
|
||||||
continue
|
|
||||||
if skip > 0:
|
|
||||||
# Due to clang-format, values may come up in the next line
|
|
||||||
skip -= 1
|
|
||||||
continue
|
|
||||||
line = line.strip()
|
|
||||||
|
|
||||||
if line.startswith(MARKUP): # markup for comments
|
|
||||||
outfile.write(("\n%s%s%s\n" %(templ['comment_open'], \
|
|
||||||
line.replace(MARKUP, ''), templ['comment_close'])).encode("utf-8"))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if line == '' or line.startswith('//'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
tmp = line.strip().split(',')
|
|
||||||
if len(tmp) >= 2 and tmp[0] != "#define" and not tmp[0].startswith("UC_"):
|
|
||||||
continue
|
|
||||||
for t in tmp:
|
|
||||||
t = t.strip()
|
|
||||||
if not t or t.startswith('//'): continue
|
|
||||||
f = re.split('\s+', t)
|
|
||||||
|
|
||||||
# parse #define UC_TARGET (num)
|
|
||||||
define = False
|
|
||||||
if f[0] == '#define' and len(f) >= 3:
|
|
||||||
define = True
|
|
||||||
f.pop(0)
|
|
||||||
f.insert(1, '=')
|
|
||||||
if f[0].startswith("UC_" + prefix.upper()) or f[0].startswith("UC_CPU"):
|
|
||||||
if len(f) > 1 and f[1] not in ('//', '='):
|
|
||||||
print("WARNING: Unable to convert %s" % f)
|
|
||||||
print(" Line =", line)
|
|
||||||
continue
|
|
||||||
elif len(f) > 1 and f[1] == '=':
|
|
||||||
# Like:
|
|
||||||
# UC_A =
|
|
||||||
# (1 << 2)
|
|
||||||
# #define UC_B \
|
|
||||||
# (UC_A | UC_C)
|
|
||||||
# Let's search the next line
|
|
||||||
if len(f) == 2:
|
|
||||||
if lno == len(lines) - 1:
|
|
||||||
print("WARNING: Unable to convert %s" % f)
|
|
||||||
print(" Line =", line)
|
|
||||||
continue
|
|
||||||
skip += 1
|
|
||||||
next_line = lines[lno + 1]
|
|
||||||
next_line_tmp = next_line.strip().split(",")
|
|
||||||
rhs = next_line_tmp[0]
|
|
||||||
elif f[-1] == "\\":
|
|
||||||
idx = 0
|
|
||||||
rhs = ""
|
|
||||||
while True:
|
|
||||||
idx += 1
|
|
||||||
if lno + idx == len(lines):
|
|
||||||
print("WARNING: Unable to convert %s" % f)
|
|
||||||
print(" Line =", line)
|
|
||||||
continue
|
|
||||||
skip += 1
|
|
||||||
next_line = lines[lno + idx]
|
|
||||||
next_line_f = re.split('\s+', next_line.strip())
|
|
||||||
if next_line_f[-1] == "\\":
|
|
||||||
rhs += "".join(next_line_f[:-1])
|
|
||||||
else:
|
|
||||||
rhs += next_line.strip()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
rhs = ''.join(f[2:])
|
|
||||||
else:
|
|
||||||
rhs = str(count)
|
|
||||||
|
|
||||||
|
|
||||||
lhs = f[0].strip()
|
|
||||||
#print(f'lhs: {lhs} rhs: {rhs} f:{f}')
|
|
||||||
# evaluate bitshifts in constants e.g. "UC_X86 = 1 << 1"
|
|
||||||
match = re.match(r'(?P<rhs>\s*\d+\s*<<\s*\d+\s*)', rhs)
|
|
||||||
if match:
|
|
||||||
rhs = str(eval(match.group(1)))
|
|
||||||
else:
|
|
||||||
# evaluate references to other constants e.g. "UC_ARM_REG_X = UC_ARM_REG_SP"
|
|
||||||
match = re.match(r'^([^\d]\w+)$', rhs)
|
|
||||||
if match:
|
|
||||||
rhs = previous[match.group(1)]
|
|
||||||
|
|
||||||
if not rhs.isdigit():
|
|
||||||
for k, v in previous.items():
|
|
||||||
rhs = re.sub(r'\b%s\b' % k, v, rhs)
|
|
||||||
rhs = str(eval(rhs))
|
|
||||||
|
|
||||||
lhs_strip = re.sub(r'^UC_', '', lhs)
|
|
||||||
count = int(rhs) + 1
|
|
||||||
|
|
||||||
if target == "unicorn.h":
|
|
||||||
matched_cat = False
|
|
||||||
for cat in split_common:
|
|
||||||
if lhs_strip.startswith(f"{cat}_"):
|
|
||||||
with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file:
|
|
||||||
cat_lhs_strip = lhs_strip
|
|
||||||
if not lhs_strip.lstrip(f"{cat}_").isnumeric():
|
|
||||||
cat_lhs_strip = lhs_strip.replace(f"{cat}_", "", 1)
|
|
||||||
cat_file.write(
|
|
||||||
(templ['line_format'] % (cat_lhs_strip, rhs)).encode("utf-8"))
|
|
||||||
matched_cat = True
|
|
||||||
break
|
|
||||||
if matched_cat:
|
|
||||||
previous[lhs] = str(rhs)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if (count == 1):
|
|
||||||
outfile.write(("\n").encode("utf-8"))
|
|
||||||
|
|
||||||
if lhs_strip.startswith(f"{prefix.upper()}_") and not lhs_strip.replace(f"{prefix.upper()}_", "", 1).isnumeric():
|
|
||||||
lhs_strip = lhs_strip.replace(f"{prefix.upper()}_", "", 1)
|
|
||||||
|
|
||||||
outfile.write((templ['line_format'] % (lhs_strip, rhs)).encode("utf-8"))
|
|
||||||
previous[lhs] = str(rhs)
|
|
||||||
|
|
||||||
outfile.write((templ['footer']).encode("utf-8"))
|
|
||||||
outfile.close()
|
|
||||||
|
|
||||||
if target == "unicorn.h":
|
|
||||||
for cat in split_common:
|
|
||||||
with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file:
|
|
||||||
cat_file.write(templ['footer'].encode('utf-8'))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage:", sys.argv[0], " <path to unicorn repo>")
|
|
||||||
sys.exit(1)
|
|
||||||
unicorn_repo_path = sys.argv[1]
|
|
||||||
if os.path.isdir(unicorn_repo_path):
|
|
||||||
print("Generating constants for dotnet")
|
|
||||||
gen(unicorn_repo_path)
|
|
||||||
else:
|
|
||||||
print("Couldn't find unicorn repo at:", unicorn_repo_path)
|
|
@@ -6,7 +6,6 @@ using Ryujinx.Cpu.Jit;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Tests.Unicorn;
|
using Ryujinx.Tests.Unicorn;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission;
|
using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Cpu
|
namespace Ryujinx.Tests.Cpu
|
||||||
@@ -33,18 +32,10 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
private CpuContext _cpuContext;
|
private CpuContext _cpuContext;
|
||||||
|
|
||||||
private static bool _unicornAvailable;
|
|
||||||
private UnicornAArch64 _unicornEmu;
|
private UnicornAArch64 _unicornEmu;
|
||||||
|
|
||||||
private bool _usingMemory;
|
private bool _usingMemory;
|
||||||
|
|
||||||
[OneTimeSetUp]
|
|
||||||
public void OneTimeSetup()
|
|
||||||
{
|
|
||||||
_unicornAvailable = UnicornAArch64.IsAvailable();
|
|
||||||
Assume.That(_unicornAvailable, "Unicorn is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
@@ -65,23 +56,17 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
Optimizations.AllowLcqInFunctionTable = false;
|
Optimizations.AllowLcqInFunctionTable = false;
|
||||||
Optimizations.UseUnmanagedDispatchLoop = false;
|
Optimizations.UseUnmanagedDispatchLoop = false;
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu = new UnicornAArch64();
|
_unicornEmu = new UnicornAArch64();
|
||||||
_unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.READ | MemoryPermission.EXEC);
|
_unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Exec);
|
||||||
_unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.READ | MemoryPermission.WRITE);
|
_unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Write);
|
||||||
_unicornEmu.PC = CodeBaseAddress;
|
_unicornEmu.PC = CodeBaseAddress;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void Teardown()
|
public void Teardown()
|
||||||
{
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
{
|
||||||
_unicornEmu.Dispose();
|
_unicornEmu.Dispose();
|
||||||
_unicornEmu = null;
|
_unicornEmu = null;
|
||||||
}
|
|
||||||
|
|
||||||
_memory.DecrementReferenceCount();
|
_memory.DecrementReferenceCount();
|
||||||
_context.Dispose();
|
_context.Dispose();
|
||||||
@@ -105,10 +90,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
{
|
{
|
||||||
_memory.Write(_currAddress, opcode);
|
_memory.Write(_currAddress, opcode);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.MemoryWrite32(_currAddress, opcode);
|
_unicornEmu.MemoryWrite32(_currAddress, opcode);
|
||||||
}
|
|
||||||
|
|
||||||
_currAddress += 4;
|
_currAddress += 4;
|
||||||
}
|
}
|
||||||
@@ -158,8 +140,6 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
_context.Fpcr = (FPCR)fpcr;
|
_context.Fpcr = (FPCR)fpcr;
|
||||||
_context.Fpsr = (FPSR)fpsr;
|
_context.Fpsr = (FPSR)fpsr;
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.X[0] = x0;
|
_unicornEmu.X[0] = x0;
|
||||||
_unicornEmu.X[1] = x1;
|
_unicornEmu.X[1] = x1;
|
||||||
_unicornEmu.X[2] = x2;
|
_unicornEmu.X[2] = x2;
|
||||||
@@ -183,13 +163,12 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
_unicornEmu.Fpcr = fpcr;
|
_unicornEmu.Fpcr = fpcr;
|
||||||
_unicornEmu.Fpsr = fpsr;
|
_unicornEmu.Fpsr = fpsr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected void ExecuteOpcodes(bool runUnicorn = true)
|
protected void ExecuteOpcodes(bool runUnicorn = true)
|
||||||
{
|
{
|
||||||
_cpuContext.Execute(_context, CodeBaseAddress);
|
_cpuContext.Execute(_context, CodeBaseAddress);
|
||||||
|
|
||||||
if (_unicornAvailable && runUnicorn)
|
if (runUnicorn)
|
||||||
{
|
{
|
||||||
_unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4);
|
_unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4);
|
||||||
}
|
}
|
||||||
@@ -239,10 +218,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
{
|
{
|
||||||
_memory.Write(DataBaseAddress + offset, data);
|
_memory.Write(DataBaseAddress + offset, data);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.MemoryWrite(DataBaseAddress + offset, data);
|
_unicornEmu.MemoryWrite(DataBaseAddress + offset, data);
|
||||||
}
|
|
||||||
|
|
||||||
_usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
|
_usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
|
||||||
}
|
}
|
||||||
@@ -251,10 +227,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
{
|
{
|
||||||
_memory.Write(DataBaseAddress + offset, data);
|
_memory.Write(DataBaseAddress + offset, data);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.MemoryWrite8(DataBaseAddress + offset, data);
|
_unicornEmu.MemoryWrite8(DataBaseAddress + offset, data);
|
||||||
}
|
|
||||||
|
|
||||||
_usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
|
_usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
|
||||||
}
|
}
|
||||||
@@ -331,11 +304,6 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
FpSkips fpSkips = FpSkips.None,
|
FpSkips fpSkips = FpSkips.None,
|
||||||
FpTolerances fpTolerances = FpTolerances.None)
|
FpTolerances fpTolerances = FpTolerances.None)
|
||||||
{
|
{
|
||||||
if (!_unicornAvailable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IgnoreAllExcept_FpsrQc)
|
if (IgnoreAllExcept_FpsrQc)
|
||||||
{
|
{
|
||||||
fpsrMask &= Fpsr.Qc;
|
fpsrMask &= Fpsr.Qc;
|
||||||
|
@@ -6,7 +6,6 @@ using Ryujinx.Cpu.Jit;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Tests.Unicorn;
|
using Ryujinx.Tests.Unicorn;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission;
|
using MemoryPermission = Ryujinx.Tests.Unicorn.MemoryPermission;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Cpu
|
namespace Ryujinx.Tests.Cpu
|
||||||
@@ -27,19 +26,10 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
private ExecutionContext _context;
|
private ExecutionContext _context;
|
||||||
|
|
||||||
private CpuContext _cpuContext;
|
private CpuContext _cpuContext;
|
||||||
|
|
||||||
private static bool _unicornAvailable;
|
|
||||||
private UnicornAArch32 _unicornEmu;
|
private UnicornAArch32 _unicornEmu;
|
||||||
|
|
||||||
private bool _usingMemory;
|
private bool _usingMemory;
|
||||||
|
|
||||||
[OneTimeSetUp]
|
|
||||||
public void OneTimeSetup()
|
|
||||||
{
|
|
||||||
_unicornAvailable = UnicornAArch32.IsAvailable();
|
|
||||||
Assume.That(_unicornAvailable, "Unicorn is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
@@ -61,23 +51,17 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
Optimizations.AllowLcqInFunctionTable = false;
|
Optimizations.AllowLcqInFunctionTable = false;
|
||||||
Optimizations.UseUnmanagedDispatchLoop = false;
|
Optimizations.UseUnmanagedDispatchLoop = false;
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu = new UnicornAArch32();
|
_unicornEmu = new UnicornAArch32();
|
||||||
_unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.READ | MemoryPermission.EXEC);
|
_unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Exec);
|
||||||
_unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.READ | MemoryPermission.WRITE);
|
_unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.Read | MemoryPermission.Write);
|
||||||
_unicornEmu.PC = CodeBaseAddress;
|
_unicornEmu.PC = CodeBaseAddress;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void Teardown()
|
public void Teardown()
|
||||||
{
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
{
|
||||||
_unicornEmu.Dispose();
|
_unicornEmu.Dispose();
|
||||||
_unicornEmu = null;
|
_unicornEmu = null;
|
||||||
}
|
|
||||||
|
|
||||||
_memory.DecrementReferenceCount();
|
_memory.DecrementReferenceCount();
|
||||||
_context.Dispose();
|
_context.Dispose();
|
||||||
@@ -101,10 +85,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
{
|
{
|
||||||
_memory.Write(_currAddress, opcode);
|
_memory.Write(_currAddress, opcode);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.MemoryWrite32(_currAddress, opcode);
|
_unicornEmu.MemoryWrite32(_currAddress, opcode);
|
||||||
}
|
|
||||||
|
|
||||||
_currAddress += 4;
|
_currAddress += 4;
|
||||||
}
|
}
|
||||||
@@ -113,10 +94,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
{
|
{
|
||||||
_memory.Write(_currAddress, opcode);
|
_memory.Write(_currAddress, opcode);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.MemoryWrite16(_currAddress, opcode);
|
_unicornEmu.MemoryWrite16(_currAddress, opcode);
|
||||||
}
|
|
||||||
|
|
||||||
_currAddress += 2;
|
_currAddress += 2;
|
||||||
}
|
}
|
||||||
@@ -169,8 +147,6 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
_context.SetPstateFlag(PState.TFlag, thumb);
|
_context.SetPstateFlag(PState.TFlag, thumb);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.R[0] = r0;
|
_unicornEmu.R[0] = r0;
|
||||||
_unicornEmu.R[1] = r1;
|
_unicornEmu.R[1] = r1;
|
||||||
_unicornEmu.R[2] = r2;
|
_unicornEmu.R[2] = r2;
|
||||||
@@ -196,13 +172,12 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
_unicornEmu.ThumbFlag = thumb;
|
_unicornEmu.ThumbFlag = thumb;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected void ExecuteOpcodes(bool runUnicorn = true)
|
protected void ExecuteOpcodes(bool runUnicorn = true)
|
||||||
{
|
{
|
||||||
_cpuContext.Execute(_context, CodeBaseAddress);
|
_cpuContext.Execute(_context, CodeBaseAddress);
|
||||||
|
|
||||||
if (_unicornAvailable && runUnicorn)
|
if (runUnicorn)
|
||||||
{
|
{
|
||||||
_unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4);
|
_unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4);
|
||||||
}
|
}
|
||||||
@@ -322,10 +297,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
{
|
{
|
||||||
_memory.Write(DataBaseAddress + offset, data);
|
_memory.Write(DataBaseAddress + offset, data);
|
||||||
|
|
||||||
if (_unicornAvailable)
|
|
||||||
{
|
|
||||||
_unicornEmu.MemoryWrite(DataBaseAddress + offset, data);
|
_unicornEmu.MemoryWrite(DataBaseAddress + offset, data);
|
||||||
}
|
|
||||||
|
|
||||||
_usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
|
_usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
|
||||||
}
|
}
|
||||||
@@ -407,11 +379,6 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
FpSkips fpSkips = FpSkips.None,
|
FpSkips fpSkips = FpSkips.None,
|
||||||
FpTolerances fpTolerances = FpTolerances.None)
|
FpTolerances fpTolerances = FpTolerances.None)
|
||||||
{
|
{
|
||||||
if (!_unicornAvailable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fpSkips != FpSkips.None)
|
if (fpSkips != FpSkips.None)
|
||||||
{
|
{
|
||||||
ManageFpSkips(fpSkips);
|
ManageFpSkips(fpSkips);
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#define Alu
|
#define Alu
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Cpu
|
namespace Ryujinx.Tests.Cpu
|
||||||
@@ -91,12 +90,10 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private const int RndCnt = 2;
|
|
||||||
|
|
||||||
[Test, Pairwise, Description("CLS <Xd>, <Xn>")]
|
[Test, Pairwise, Description("CLS <Xd>, <Xn>")]
|
||||||
public void Cls_64bit([Values(0u, 31u)] uint rd,
|
public void Cls_64bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[ValueSource("_GenLeadingSignsX_")] [Random(RndCnt)] ulong xn)
|
[ValueSource(nameof(_GenLeadingSignsX_))] ulong xn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xDAC01400; // CLS X0, X0
|
uint opcode = 0xDAC01400; // CLS X0, X0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -111,7 +108,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Test, Pairwise, Description("CLS <Wd>, <Wn>")]
|
[Test, Pairwise, Description("CLS <Wd>, <Wn>")]
|
||||||
public void Cls_32bit([Values(0u, 31u)] uint rd,
|
public void Cls_32bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[ValueSource("_GenLeadingSignsW_")] [Random(RndCnt)] uint wn)
|
[ValueSource(nameof(_GenLeadingSignsW_))] uint wn)
|
||||||
{
|
{
|
||||||
uint opcode = 0x5AC01400; // CLS W0, W0
|
uint opcode = 0x5AC01400; // CLS W0, W0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -126,7 +123,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Test, Pairwise, Description("CLZ <Xd>, <Xn>")]
|
[Test, Pairwise, Description("CLZ <Xd>, <Xn>")]
|
||||||
public void Clz_64bit([Values(0u, 31u)] uint rd,
|
public void Clz_64bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[ValueSource("_GenLeadingZerosX_")] [Random(RndCnt)] ulong xn)
|
[ValueSource(nameof(_GenLeadingZerosX_))] ulong xn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xDAC01000; // CLZ X0, X0
|
uint opcode = 0xDAC01000; // CLZ X0, X0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -141,7 +138,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Test, Pairwise, Description("CLZ <Wd>, <Wn>")]
|
[Test, Pairwise, Description("CLZ <Wd>, <Wn>")]
|
||||||
public void Clz_32bit([Values(0u, 31u)] uint rd,
|
public void Clz_32bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[ValueSource("_GenLeadingZerosW_")] [Random(RndCnt)] uint wn)
|
[ValueSource(nameof(_GenLeadingZerosW_))] uint wn)
|
||||||
{
|
{
|
||||||
uint opcode = 0x5AC01000; // CLZ W0, W0
|
uint opcode = 0x5AC01000; // CLZ W0, W0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -157,7 +154,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rbit_64bit([Values(0u, 31u)] uint rd,
|
public void Rbit_64bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn)
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xDAC00000; // RBIT X0, X0
|
uint opcode = 0xDAC00000; // RBIT X0, X0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -173,7 +170,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rbit_32bit([Values(0u, 31u)] uint rd,
|
public void Rbit_32bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
0x80000000u, 0xFFFFFFFFu)] uint wn)
|
||||||
{
|
{
|
||||||
uint opcode = 0x5AC00000; // RBIT W0, W0
|
uint opcode = 0x5AC00000; // RBIT W0, W0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -189,7 +186,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rev16_64bit([Values(0u, 31u)] uint rd,
|
public void Rev16_64bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn)
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xDAC00400; // REV16 X0, X0
|
uint opcode = 0xDAC00400; // REV16 X0, X0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -205,7 +202,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rev16_32bit([Values(0u, 31u)] uint rd,
|
public void Rev16_32bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
0x80000000u, 0xFFFFFFFFu)] uint wn)
|
||||||
{
|
{
|
||||||
uint opcode = 0x5AC00400; // REV16 W0, W0
|
uint opcode = 0x5AC00400; // REV16 W0, W0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -221,7 +218,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rev32_64bit([Values(0u, 31u)] uint rd,
|
public void Rev32_64bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn)
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xDAC00800; // REV32 X0, X0
|
uint opcode = 0xDAC00800; // REV32 X0, X0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -237,7 +234,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rev32_32bit([Values(0u, 31u)] uint rd,
|
public void Rev32_32bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
0x80000000u, 0xFFFFFFFFu)] uint wn)
|
||||||
{
|
{
|
||||||
uint opcode = 0x5AC00800; // REV W0, W0
|
uint opcode = 0x5AC00800; // REV W0, W0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -253,7 +250,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rev64_64bit([Values(0u, 31u)] uint rd,
|
public void Rev64_64bit([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn)
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xDAC00C00; // REV64 X0, X0
|
uint opcode = 0xDAC00C00; // REV64 X0, X0
|
||||||
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
|
@@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
#region "ValueSource (Opcodes)"
|
#region "ValueSource (Opcodes)"
|
||||||
private static uint[] _SU_H_AddSub_8_()
|
private static uint[] _SU_H_AddSub_8_()
|
||||||
{
|
{
|
||||||
return new uint[]
|
return new[]
|
||||||
{
|
{
|
||||||
0xe6100f90u, // SADD8 R0, R0, R0
|
0xe6100f90u, // SADD8 R0, R0, R0
|
||||||
0xe6100ff0u, // SSUB8 R0, R0, R0
|
0xe6100ff0u, // SSUB8 R0, R0, R0
|
||||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
private static uint[] _Ssat_Usat_()
|
private static uint[] _Ssat_Usat_()
|
||||||
{
|
{
|
||||||
return new uint[]
|
return new[]
|
||||||
{
|
{
|
||||||
0xe6a00010u, // SSAT R0, #1, R0, LSL #0
|
0xe6a00010u, // SSAT R0, #1, R0, LSL #0
|
||||||
0xe6a00050u, // SSAT R0, #1, R0, ASR #32
|
0xe6a00050u, // SSAT R0, #1, R0, ASR #32
|
||||||
@@ -38,7 +38,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
private static uint[] _Ssat16_Usat16_()
|
private static uint[] _Ssat16_Usat16_()
|
||||||
{
|
{
|
||||||
return new uint[]
|
return new[]
|
||||||
{
|
{
|
||||||
0xe6a00f30u, // SSAT16 R0, #1, R0
|
0xe6a00f30u, // SSAT16 R0, #1, R0
|
||||||
0xe6e00f30u, // USAT16 R0, #0, R0
|
0xe6e00f30u, // USAT16 R0, #0, R0
|
||||||
@@ -47,7 +47,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
private static uint[] _Lsr_Lsl_Asr_Ror_()
|
private static uint[] _Lsr_Lsl_Asr_Ror_()
|
||||||
{
|
{
|
||||||
return new uint[]
|
return new[]
|
||||||
{
|
{
|
||||||
0xe1b00030u, // LSRS R0, R0, R0
|
0xe1b00030u, // LSRS R0, R0, R0
|
||||||
0xe1b00010u, // LSLS R0, R0, R0
|
0xe1b00010u, // LSLS R0, R0, R0
|
||||||
@@ -63,7 +63,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Rbit_32bit([Values(0u, 0xdu)] uint rd,
|
public void Rbit_32bit([Values(0u, 0xdu)] uint rd,
|
||||||
[Values(1u, 0xdu)] uint rm,
|
[Values(1u, 0xdu)] uint rm,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
0x80000000u, 0xFFFFFFFFu)] uint wn)
|
||||||
{
|
{
|
||||||
uint opcode = 0xe6ff0f30u; // RBIT R0, R0
|
uint opcode = 0xe6ff0f30u; // RBIT R0, R0
|
||||||
opcode |= ((rm & 15) << 0) | ((rd & 15) << 12);
|
opcode |= ((rm & 15) << 0) | ((rd & 15) << 12);
|
||||||
@@ -76,10 +76,10 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
public void Lsr_Lsl_Asr_Ror([ValueSource("_Lsr_Lsl_Asr_Ror_")] uint opcode,
|
public void Lsr_Lsl_Asr_Ror([ValueSource(nameof(_Lsr_Lsl_Asr_Ror_))] uint opcode,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue,
|
0x80000000u, 0xFFFFFFFFu)] uint shiftValue,
|
||||||
[Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount)
|
[Range(0, 31)] int shiftAmount)
|
||||||
{
|
{
|
||||||
uint rd = 0;
|
uint rd = 0;
|
||||||
uint rm = 1;
|
uint rm = 1;
|
||||||
@@ -130,13 +130,13 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
public void Ssat_Usat([ValueSource("_Ssat_Usat_")] uint opcode,
|
public void Ssat_Usat([ValueSource(nameof(_Ssat_Usat_))] uint opcode,
|
||||||
[Values(0u, 0xdu)] uint rd,
|
[Values(0u, 0xdu)] uint rd,
|
||||||
[Values(1u, 0xdu)] uint rn,
|
[Values(1u, 0xdu)] uint rn,
|
||||||
[Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint sat,
|
[Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint sat,
|
||||||
[Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint shift,
|
[Values(0u, 7u, 8u, 0xfu, 0x10u, 0x1fu)] uint shift,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
0x80000000u, 0xFFFFFFFFu)] uint wn)
|
||||||
{
|
{
|
||||||
opcode |= ((rn & 15) << 0) | ((shift & 31) << 7) | ((rd & 15) << 12) | ((sat & 31) << 16);
|
opcode |= ((rn & 15) << 0) | ((shift & 31) << 7) | ((rd & 15) << 12) | ((sat & 31) << 16);
|
||||||
|
|
||||||
@@ -148,12 +148,12 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
public void Ssat16_Usat16([ValueSource("_Ssat16_Usat16_")] uint opcode,
|
public void Ssat16_Usat16([ValueSource(nameof(_Ssat16_Usat16_))] uint opcode,
|
||||||
[Values(0u, 0xdu)] uint rd,
|
[Values(0u, 0xdu)] uint rd,
|
||||||
[Values(1u, 0xdu)] uint rn,
|
[Values(1u, 0xdu)] uint rn,
|
||||||
[Values(0u, 7u, 8u, 0xfu)] uint sat,
|
[Values(0u, 7u, 8u, 0xfu)] uint sat,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
|
0x80000000u, 0xFFFFFFFFu)] uint wn)
|
||||||
{
|
{
|
||||||
opcode |= ((rn & 15) << 0) | ((rd & 15) << 12) | ((sat & 15) << 16);
|
opcode |= ((rn & 15) << 0) | ((rd & 15) << 12) | ((sat & 15) << 16);
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
public void SU_H_AddSub_8([ValueSource("_SU_H_AddSub_8_")] uint opcode,
|
public void SU_H_AddSub_8([ValueSource(nameof(_SU_H_AddSub_8_))] uint opcode,
|
||||||
[Values(0u, 0xdu)] uint rd,
|
[Values(0u, 0xdu)] uint rd,
|
||||||
[Values(1u)] uint rm,
|
[Values(1u)] uint rm,
|
||||||
[Values(2u)] uint rn,
|
[Values(2u)] uint rn,
|
||||||
|
@@ -36,7 +36,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
// - xor 0
|
// - xor 0
|
||||||
// Only includes non-C variant, as the other can be tested with unicorn.
|
// Only includes non-C variant, as the other can be tested with unicorn.
|
||||||
|
|
||||||
return new CrcTest[]
|
return new[]
|
||||||
{
|
{
|
||||||
new CrcTest(0x00000000u, 0x00_00_00_00_00_00_00_00u, false, 0x00000000, 0x00000000, 0x00000000, 0x00000000),
|
new CrcTest(0x00000000u, 0x00_00_00_00_00_00_00_00u, false, 0x00000000, 0x00000000, 0x00000000, 0x00000000),
|
||||||
new CrcTest(0x00000000u, 0x7f_ff_ff_ff_ff_ff_ff_ffu, false, 0x2d02ef8d, 0xbe2612ff, 0xdebb20e3, 0xa9de8355),
|
new CrcTest(0x00000000u, 0x7f_ff_ff_ff_ff_ff_ff_ffu, false, 0x2d02ef8d, 0xbe2612ff, 0xdebb20e3, 0xa9de8355),
|
||||||
@@ -53,14 +53,12 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private const int RndCnt = 2;
|
|
||||||
|
|
||||||
[Test, Combinatorial]
|
[Test, Combinatorial]
|
||||||
public void Crc32_b_h_w_x([Values(0u)] uint rd,
|
public void Crc32_b_h_w_x([Values(0u)] uint rd,
|
||||||
[Values(1u)] uint rn,
|
[Values(1u)] uint rn,
|
||||||
[Values(2u)] uint rm,
|
[Values(2u)] uint rm,
|
||||||
[Range(0u, 3u)] uint size,
|
[Range(0u, 3u)] uint size,
|
||||||
[ValueSource("_CRC32_Test_Values_")] CrcTest test)
|
[ValueSource(nameof(_CRC32_Test_Values_))] CrcTest test)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC04000; // CRC32B W0, W0, W0
|
uint opcode = 0x1AC04000; // CRC32B W0, W0, W0
|
||||||
|
|
||||||
@@ -85,11 +83,11 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32x([Values(0u, 31u)] uint rd,
|
public void Crc32x([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((ulong)0x00_00_00_00_00_00_00_00,
|
[Values((ulong)0x00_00_00_00_00_00_00_00,
|
||||||
(ulong)0x7F_FF_FF_FF_FF_FF_FF_FF,
|
(ulong)0x7F_FF_FF_FF_FF_FF_FF_FF,
|
||||||
(ulong)0x80_00_00_00_00_00_00_00,
|
0x80_00_00_00_00_00_00_00,
|
||||||
(ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong xm)
|
0xFF_FF_FF_FF_FF_FF_FF_FF)] ulong xm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x9AC04C00; // CRC32X W0, W0, X0
|
uint opcode = 0x9AC04C00; // CRC32X W0, W0, X0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -105,9 +103,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32w([Values(0u, 31u)] uint rd,
|
public void Crc32w([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF,
|
[Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF,
|
||||||
(uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint wm)
|
0x80_00_00_00, 0xFF_FF_FF_FF)] uint wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC04800; // CRC32W W0, W0, W0
|
uint opcode = 0x1AC04800; // CRC32W W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -123,9 +121,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32h([Values(0u, 31u)] uint rd,
|
public void Crc32h([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((ushort)0x00_00, (ushort)0x7F_FF,
|
[Values((ushort)0x00_00, (ushort)0x7F_FF,
|
||||||
(ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort wm)
|
(ushort)0x80_00, (ushort)0xFF_FF)] ushort wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC04400; // CRC32H W0, W0, W0
|
uint opcode = 0x1AC04400; // CRC32H W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -141,9 +139,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32b([Values(0u, 31u)] uint rd,
|
public void Crc32b([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((byte)0x00, (byte)0x7F,
|
[Values((byte)0x00, (byte)0x7F,
|
||||||
(byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm)
|
(byte)0x80, (byte)0xFF)] byte wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC04000; // CRC32B W0, W0, W0
|
uint opcode = 0x1AC04000; // CRC32B W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -159,11 +157,11 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32cx([Values(0u, 31u)] uint rd,
|
public void Crc32cx([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((ulong)0x00_00_00_00_00_00_00_00,
|
[Values((ulong)0x00_00_00_00_00_00_00_00,
|
||||||
(ulong)0x7F_FF_FF_FF_FF_FF_FF_FF,
|
(ulong)0x7F_FF_FF_FF_FF_FF_FF_FF,
|
||||||
(ulong)0x80_00_00_00_00_00_00_00,
|
0x80_00_00_00_00_00_00_00,
|
||||||
(ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong xm)
|
0xFF_FF_FF_FF_FF_FF_FF_FF)] ulong xm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x9AC05C00; // CRC32CX W0, W0, X0
|
uint opcode = 0x9AC05C00; // CRC32CX W0, W0, X0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -179,9 +177,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32cw([Values(0u, 31u)] uint rd,
|
public void Crc32cw([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF,
|
[Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF,
|
||||||
(uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint wm)
|
0x80_00_00_00, 0xFF_FF_FF_FF)] uint wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC05800; // CRC32CW W0, W0, W0
|
uint opcode = 0x1AC05800; // CRC32CW W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -197,9 +195,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32ch([Values(0u, 31u)] uint rd,
|
public void Crc32ch([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((ushort)0x00_00, (ushort)0x7F_FF,
|
[Values((ushort)0x00_00, (ushort)0x7F_FF,
|
||||||
(ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort wm)
|
(ushort)0x80_00, (ushort)0xFF_FF)] ushort wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC05400; // CRC32CH W0, W0, W0
|
uint opcode = 0x1AC05400; // CRC32CH W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -215,9 +213,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
public void Crc32cb([Values(0u, 31u)] uint rd,
|
public void Crc32cb([Values(0u, 31u)] uint rd,
|
||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
[Values(0x00000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values((byte)0x00, (byte)0x7F,
|
[Values((byte)0x00, (byte)0x7F,
|
||||||
(byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm)
|
(byte)0x80, (byte)0xFF)] byte wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC05000; // CRC32CB W0, W0, W0
|
uint opcode = 0x1AC05000; // CRC32CB W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -234,9 +232,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn,
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm)
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x9AC00C00; // SDIV X0, X0, X0
|
uint opcode = 0x9AC00C00; // SDIV X0, X0, X0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -253,9 +251,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
0x80000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm)
|
0x80000000u, 0xFFFFFFFFu)] uint wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC00C00; // SDIV W0, W0, W0
|
uint opcode = 0x1AC00C00; // SDIV W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -272,9 +270,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn,
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xn,
|
||||||
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
[Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm)
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] ulong xm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x9AC00800; // UDIV X0, X0, X0
|
uint opcode = 0x9AC00800; // UDIV X0, X0, X0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
@@ -291,9 +289,9 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
[Values(1u, 31u)] uint rn,
|
[Values(1u, 31u)] uint rn,
|
||||||
[Values(2u, 31u)] uint rm,
|
[Values(2u, 31u)] uint rm,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
|
0x80000000u, 0xFFFFFFFFu)] uint wn,
|
||||||
[Values(0x00000000u, 0x7FFFFFFFu,
|
[Values(0x00000000u, 0x7FFFFFFFu,
|
||||||
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm)
|
0x80000000u, 0xFFFFFFFFu)] uint wm)
|
||||||
{
|
{
|
||||||
uint opcode = 0x1AC00800; // UDIV W0, W0, W0
|
uint opcode = 0x1AC00800; // UDIV W0, W0, W0
|
||||||
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user