Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
f2a41b7a1c | |||
c881cd2d14 | |||
68f9091870 | |||
99ffc061d3 | |||
d987cacfb7 | |||
851f56b08a | |||
b1bd6a50b5 | |||
70895bdb04 | |||
830cbf91bb | |||
9a9349f0f4 | |||
46cc7b55f0 | |||
dd8f97ab9e | |||
633c5ec330 | |||
a3e7bb8eb4 | |||
2073ba2919 | |||
d03124a992 | |||
59490d54b5 | |||
e546e5933f | |||
0c87bf9ea4 | |||
9827dc35e1 | |||
448723d3b3 | |||
89294b7772 | |||
7b9c4757dd | |||
b8fc97adf2 | |||
c1a7b5bcdb | |||
be1c375589 | |||
378d19f87a |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
OS_NAME: Windows x64
|
OS_NAME: Windows x64
|
||||||
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||||
RELEASE_ZIP_OS_NAME: win_x64
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
7
.github/workflows/nightly_pr_comment.yml
vendored
7
.github/workflows/nightly_pr_comment.yml
vendored
@ -36,19 +36,24 @@ jobs:
|
|||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
let body = `Download the artifacts for this pull request:\n`;
|
||||||
|
let hidden_avalonia_artifacts = `\n\n <details><summary>Experimental GUI (Avalonia)</summary>\n`;
|
||||||
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
if(art.name.includes('Debug')) {
|
if(art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else if(art.name.includes('headless-sdl2')) {
|
} else if(art.name.includes('ava-ryujinx')) {
|
||||||
|
hidden_avalonia_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
|
} else if(art.name.includes('sdl2-ryujinx-headless')) {
|
||||||
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
} else {
|
} else {
|
||||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hidden_avalonia_artifacts += `\n</details>`;
|
||||||
hidden_headless_artifacts += `\n</details>`;
|
hidden_headless_artifacts += `\n</details>`;
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
hidden_debug_artifacts += `\n</details>`;
|
||||||
|
body += hidden_avalonia_artifacts;
|
||||||
body += hidden_headless_artifacts;
|
body += hidden_headless_artifacts;
|
||||||
body += hidden_debug_artifacts;
|
body += hidden_debug_artifacts;
|
||||||
|
|
||||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -51,9 +51,9 @@ jobs:
|
|||||||
run: "mkdir release_output"
|
run: "mkdir release_output"
|
||||||
- name: Publish Windows
|
- name: Publish Windows
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r win-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
||||||
dotnet publish -c Release -r win-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
|
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
|
||||||
dotnet publish -c Release -r win-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
|
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
run: |
|
run: |
|
||||||
pushd publish_windows
|
pushd publish_windows
|
||||||
@ -61,11 +61,11 @@ jobs:
|
|||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_windows_sdl2_headless
|
pushd publish_windows_sdl2_headless
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_windows_ava
|
pushd publish_windows_ava
|
||||||
7z a ../release_output/ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ jobs:
|
|||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_linux_ava
|
pushd publish_linux_ava
|
||||||
tar -czvf ../release_output/ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
@ -33,13 +33,13 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
switch (GetPackedId(op))
|
switch (GetPackedId(op))
|
||||||
{
|
{
|
||||||
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
|
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
|
||||||
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
|
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
|
||||||
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
||||||
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
|
case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpcr)); break;
|
||||||
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
|
case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)); break;
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
||||||
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)); break;
|
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
|
||||||
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
||||||
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
||||||
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
||||||
|
@ -107,14 +107,14 @@ namespace ARMeilleure.Instructions
|
|||||||
return (uint)GetContext().TpidrEl0;
|
return (uint)GetContext().TpidrEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong GetTpidr()
|
public static ulong GetTpidrroEl0()
|
||||||
{
|
{
|
||||||
return (ulong)GetContext().Tpidr;
|
return (ulong)GetContext().TpidrroEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint GetTpidr32()
|
public static uint GetTpidr32()
|
||||||
{
|
{
|
||||||
return (uint)GetContext().Tpidr;
|
return (uint)GetContext().TpidrroEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong GetCntfrqEl0()
|
public static ulong GetCntfrqEl0()
|
||||||
|
@ -14,10 +14,11 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
public byte Kind;
|
public byte Kind;
|
||||||
public byte Type;
|
public byte Type;
|
||||||
public byte SymbolType;
|
public byte SymbolType;
|
||||||
|
public byte Padding; // Unused space.
|
||||||
public ushort AssignmentsCount;
|
public ushort AssignmentsCount;
|
||||||
public ushort AssignmentsCapacity;
|
public ushort AssignmentsCapacity;
|
||||||
public ushort UsesCount;
|
public uint UsesCount;
|
||||||
public ushort UsesCapacity;
|
public uint UsesCapacity;
|
||||||
public Operation* Assignments;
|
public Operation* Assignments;
|
||||||
public Operation* Uses;
|
public Operation* Uses;
|
||||||
public ulong Value;
|
public ulong Value;
|
||||||
@ -84,11 +85,11 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
{
|
{
|
||||||
Debug.Assert(Kind != OperandKind.Memory);
|
Debug.Assert(Kind != OperandKind.Memory);
|
||||||
|
|
||||||
return new ReadOnlySpan<Operation>(_data->Uses, _data->UsesCount);
|
return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int UsesCount => _data->UsesCount;
|
public int UsesCount => (int)_data->UsesCount;
|
||||||
public int AssignmentsCount => _data->AssignmentsCount;
|
public int AssignmentsCount => _data->AssignmentsCount;
|
||||||
|
|
||||||
public bool Relocatable => Symbol.Type != SymbolType.None;
|
public bool Relocatable => Symbol.Type != SymbolType.None;
|
||||||
@ -178,7 +179,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
{
|
{
|
||||||
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
|
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index != default)
|
if (index != default)
|
||||||
{
|
{
|
||||||
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
|
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
|
||||||
@ -265,6 +266,13 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
data = Allocators.References.Allocate<T>(initialCapacity);
|
data = Allocators.References.Allocate<T>(initialCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
capacity = initialCapacity;
|
||||||
|
data = Allocators.References.Allocate<T>(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
|
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
|
||||||
{
|
{
|
||||||
if (count < capacity)
|
if (count < capacity)
|
||||||
@ -294,6 +302,40 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (count < capacity)
|
||||||
|
{
|
||||||
|
data[count++] = item;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Could not add item in the fast path, fallback onto the slow path.
|
||||||
|
ExpandAdd(item, ref data, ref count, ref capacity);
|
||||||
|
|
||||||
|
static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
|
||||||
|
{
|
||||||
|
uint newCount = checked(count + 1);
|
||||||
|
uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
|
||||||
|
|
||||||
|
if (newCapacity <= capacity)
|
||||||
|
{
|
||||||
|
throw new OverflowException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldSpan = new Span<T>(data, (int)count);
|
||||||
|
|
||||||
|
capacity = newCapacity;
|
||||||
|
data = Allocators.References.Allocate<T>(capacity);
|
||||||
|
|
||||||
|
oldSpan.CopyTo(new Span<T>(data, (int)count));
|
||||||
|
|
||||||
|
data[count] = item;
|
||||||
|
count = newCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
|
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
|
||||||
{
|
{
|
||||||
var span = new Span<T>(data, count);
|
var span = new Span<T>(data, count);
|
||||||
@ -314,6 +356,26 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
|
||||||
|
{
|
||||||
|
var span = new Span<T>(data, (int)count);
|
||||||
|
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(span[i], item))
|
||||||
|
{
|
||||||
|
if (i + 1 < count)
|
||||||
|
{
|
||||||
|
span.Slice(i + 1).CopyTo(span.Slice(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
count--;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
if (Kind == OperandKind.LocalVariable)
|
if (Kind == OperandKind.LocalVariable)
|
||||||
|
5
ARMeilleure/State/ExceptionCallback.cs
Normal file
5
ARMeilleure/State/ExceptionCallback.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace ARMeilleure.State
|
||||||
|
{
|
||||||
|
public delegate void ExceptionCallbackNoArgs(ExecutionContext context);
|
||||||
|
public delegate void ExceptionCallback(ExecutionContext context, ulong address, int id);
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
@ -14,34 +13,22 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
private bool _interrupted;
|
private bool _interrupted;
|
||||||
|
|
||||||
private static Stopwatch _tickCounter;
|
private readonly ICounter _counter;
|
||||||
|
|
||||||
private static double _hostTickFreq;
|
public ulong Pc => _nativeContext.GetPc();
|
||||||
|
|
||||||
public uint CtrEl0 => 0x8444c004;
|
public uint CtrEl0 => 0x8444c004;
|
||||||
public uint DczidEl0 => 0x00000004;
|
public uint DczidEl0 => 0x00000004;
|
||||||
|
|
||||||
public ulong CntfrqEl0 { get; set; }
|
public ulong CntfrqEl0 => _counter.Frequency;
|
||||||
public ulong CntpctEl0
|
public ulong CntpctEl0 => _counter.Counter;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
double ticks = _tickCounter.ElapsedTicks * _hostTickFreq;
|
|
||||||
|
|
||||||
return (ulong)(ticks * CntfrqEl0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2
|
// CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2
|
||||||
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
||||||
public ulong CntvctEl0 => CntpctEl0;
|
public ulong CntvctEl0 => CntpctEl0;
|
||||||
|
|
||||||
public static TimeSpan ElapsedTime => _tickCounter.Elapsed;
|
|
||||||
public static long ElapsedTicks => _tickCounter.ElapsedTicks;
|
|
||||||
public static double TickFrequency => _hostTickFreq;
|
|
||||||
|
|
||||||
public long TpidrEl0 { get; set; }
|
public long TpidrEl0 { get; set; }
|
||||||
public long Tpidr { get; set; }
|
public long TpidrroEl0 { get; set; }
|
||||||
|
|
||||||
public uint Pstate
|
public uint Pstate
|
||||||
{
|
{
|
||||||
@ -78,35 +65,38 @@ namespace ARMeilleure.State
|
|||||||
private set => _nativeContext.SetRunning(value);
|
private set => _nativeContext.SetRunning(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<EventArgs> Interrupt;
|
private readonly ExceptionCallbackNoArgs _interruptCallback;
|
||||||
public event EventHandler<InstExceptionEventArgs> Break;
|
private readonly ExceptionCallback _breakCallback;
|
||||||
public event EventHandler<InstExceptionEventArgs> SupervisorCall;
|
private readonly ExceptionCallback _supervisorCallback;
|
||||||
public event EventHandler<InstUndefinedEventArgs> Undefined;
|
private readonly ExceptionCallback _undefinedCallback;
|
||||||
|
|
||||||
static ExecutionContext()
|
public ExecutionContext(
|
||||||
{
|
IJitMemoryAllocator allocator,
|
||||||
_hostTickFreq = 1.0 / Stopwatch.Frequency;
|
ICounter counter,
|
||||||
|
ExceptionCallbackNoArgs interruptCallback = null,
|
||||||
_tickCounter = new Stopwatch();
|
ExceptionCallback breakCallback = null,
|
||||||
_tickCounter.Start();
|
ExceptionCallback supervisorCallback = null,
|
||||||
}
|
ExceptionCallback undefinedCallback = null)
|
||||||
|
|
||||||
public ExecutionContext(IJitMemoryAllocator allocator)
|
|
||||||
{
|
{
|
||||||
_nativeContext = new NativeContext(allocator);
|
_nativeContext = new NativeContext(allocator);
|
||||||
|
_counter = counter;
|
||||||
|
_interruptCallback = interruptCallback;
|
||||||
|
_breakCallback = breakCallback;
|
||||||
|
_supervisorCallback = supervisorCallback;
|
||||||
|
_undefinedCallback = undefinedCallback;
|
||||||
|
|
||||||
Running = true;
|
Running = true;
|
||||||
|
|
||||||
_nativeContext.SetCounter(MinCountForCheck);
|
_nativeContext.SetCounter(MinCountForCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetX(int index) => _nativeContext.GetX(index);
|
public ulong GetX(int index) => _nativeContext.GetX(index);
|
||||||
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
|
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
|
||||||
|
|
||||||
public V128 GetV(int index) => _nativeContext.GetV(index);
|
public V128 GetV(int index) => _nativeContext.GetV(index);
|
||||||
public void SetV(int index, V128 value) => _nativeContext.SetV(index, value);
|
public void SetV(int index, V128 value) => _nativeContext.SetV(index, value);
|
||||||
|
|
||||||
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
|
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
|
||||||
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
|
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
|
||||||
|
|
||||||
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
||||||
@ -118,7 +108,7 @@ namespace ARMeilleure.State
|
|||||||
{
|
{
|
||||||
_interrupted = false;
|
_interrupted = false;
|
||||||
|
|
||||||
Interrupt?.Invoke(this, EventArgs.Empty);
|
_interruptCallback?.Invoke(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_nativeContext.SetCounter(MinCountForCheck);
|
_nativeContext.SetCounter(MinCountForCheck);
|
||||||
@ -131,17 +121,17 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
internal void OnBreak(ulong address, int imm)
|
internal void OnBreak(ulong address, int imm)
|
||||||
{
|
{
|
||||||
Break?.Invoke(this, new InstExceptionEventArgs(address, imm));
|
_breakCallback?.Invoke(this, address, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnSupervisorCall(ulong address, int imm)
|
internal void OnSupervisorCall(ulong address, int imm)
|
||||||
{
|
{
|
||||||
SupervisorCall?.Invoke(this, new InstExceptionEventArgs(address, imm));
|
_supervisorCallback?.Invoke(this, address, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnUndefined(ulong address, int opCode)
|
internal void OnUndefined(ulong address, int opCode)
|
||||||
{
|
{
|
||||||
Undefined?.Invoke(this, new InstUndefinedEventArgs(address, opCode));
|
_undefinedCallback?.Invoke(this, address, opCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopRunning()
|
public void StopRunning()
|
||||||
@ -151,16 +141,6 @@ namespace ARMeilleure.State
|
|||||||
_nativeContext.SetCounter(0);
|
_nativeContext.SetCounter(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SuspendCounter()
|
|
||||||
{
|
|
||||||
_tickCounter.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ResumeCounter()
|
|
||||||
{
|
|
||||||
_tickCounter.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_nativeContext.Dispose();
|
_nativeContext.Dispose();
|
||||||
|
18
ARMeilleure/State/ICounter.cs
Normal file
18
ARMeilleure/State/ICounter.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace ARMeilleure.State
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CPU Counter interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICounter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Counter frequency in Hertz.
|
||||||
|
/// </summary>
|
||||||
|
ulong Frequency { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current counter value.
|
||||||
|
/// </summary>
|
||||||
|
ulong Counter { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public class InstExceptionEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public ulong Address { get; }
|
|
||||||
public int Id { get; }
|
|
||||||
|
|
||||||
public InstExceptionEventArgs(ulong address, int id)
|
|
||||||
{
|
|
||||||
Address = address;
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
|
||||||
{
|
|
||||||
public class InstUndefinedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public ulong Address { get; }
|
|
||||||
public int OpCode { get; }
|
|
||||||
|
|
||||||
public InstUndefinedEventArgs(ulong address, int opCode)
|
|
||||||
{
|
|
||||||
Address = address;
|
|
||||||
OpCode = opCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,6 +34,12 @@ namespace ARMeilleure.State
|
|||||||
GetStorage().ExclusiveAddress = ulong.MaxValue;
|
GetStorage().ExclusiveAddress = ulong.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong GetPc()
|
||||||
|
{
|
||||||
|
// TODO: More precise tracking of PC value.
|
||||||
|
return GetStorage().DispatchAddress;
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe ulong GetX(int index)
|
public unsafe ulong GetX(int index)
|
||||||
{
|
{
|
||||||
if ((uint)index >= RegisterConsts.IntRegsCount)
|
if ((uint)index >= RegisterConsts.IntRegsCount)
|
||||||
|
@ -115,7 +115,7 @@ namespace ARMeilleure.Translation
|
|||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
||||||
|
@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 3267; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 3362; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -15,11 +15,11 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dll</TargetPath>
|
<TargetPath>libsoundio.dll</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dylib</TargetPath>
|
<TargetPath>libsoundio.dylib</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win10-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.so</TargetPath>
|
<TargetPath>libsoundio.so</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
|
@ -523,9 +523,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
private ulong GetSystemTicks()
|
private ulong GetSystemTicks()
|
||||||
{
|
{
|
||||||
double ticks = ARMeilleure.State.ExecutionContext.ElapsedTicks * ARMeilleure.State.ExecutionContext.TickFrequency;
|
return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
|
||||||
|
|
||||||
return (ulong)(ticks * Constants.TargetTimerFrequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)
|
private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)
|
||||||
|
@ -19,6 +19,7 @@ using Ryujinx.Audio.Integration;
|
|||||||
using Ryujinx.Audio.Renderer.Dsp;
|
using Ryujinx.Audio.Renderer.Dsp;
|
||||||
using Ryujinx.Audio.Renderer.Parameter;
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -77,6 +78,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private IHardwareDeviceDriver _deviceDriver;
|
private IHardwareDeviceDriver _deviceDriver;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tick source used to measure elapsed time.
|
||||||
|
/// </summary>
|
||||||
|
public ITickSource TickSource { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="AudioProcessor"/> instance associated to this manager.
|
/// The <see cref="AudioProcessor"/> instance associated to this manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -90,9 +96,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="AudioRendererManager"/>.
|
/// Create a new <see cref="AudioRendererManager"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AudioRendererManager()
|
/// <param name="tickSource">Tick source used to measure elapsed time.</param>
|
||||||
|
public AudioRendererManager(ITickSource tickSource)
|
||||||
{
|
{
|
||||||
Processor = new AudioProcessor();
|
Processor = new AudioProcessor();
|
||||||
|
TickSource = tickSource;
|
||||||
_sessionIds = new int[Constants.AudioRendererSessionCountMax];
|
_sessionIds = new int[Constants.AudioRendererSessionCountMax];
|
||||||
_sessions = new AudioRenderSystem[Constants.AudioRendererSessionCountMax];
|
_sessions = new AudioRenderSystem[Constants.AudioRendererSessionCountMax];
|
||||||
_activeSessionCount = 0;
|
_activeSessionCount = 0;
|
||||||
|
@ -177,7 +177,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
if (_renderer != null)
|
if (_renderer != null)
|
||||||
{
|
{
|
||||||
double scale = Program.WindowScaleFactor;
|
double scale = _parent.PlatformImpl.RenderScaling;
|
||||||
_renderer.Window.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
|
_renderer.Window.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -809,7 +809,7 @@ namespace Ryujinx.Ava
|
|||||||
Width = (int)Renderer.Bounds.Width;
|
Width = (int)Renderer.Bounds.Width;
|
||||||
Height = (int)Renderer.Bounds.Height;
|
Height = (int)Renderer.Bounds.Height;
|
||||||
|
|
||||||
_renderer.Window.SetSize((int)(Width * Program.WindowScaleFactor), (int)(Height * Program.WindowScaleFactor));
|
_renderer.Window.SetSize((int)(Width * _parent.PlatformImpl.RenderScaling), (int)(Height * _parent.PlatformImpl.RenderScaling));
|
||||||
|
|
||||||
Device.Gpu.Renderer.RunLoop(() =>
|
Device.Gpu.Renderer.RunLoop(() =>
|
||||||
{
|
{
|
||||||
|
@ -98,7 +98,7 @@ namespace Ryujinx.Modules
|
|||||||
string assetState = (string)asset["state"];
|
string assetState = (string)asset["state"];
|
||||||
string downloadURL = (string)asset["browser_download_url"];
|
string downloadURL = (string)asset["browser_download_url"];
|
||||||
|
|
||||||
if (assetName.StartsWith("ava-ryujinx") && assetName.EndsWith(_platformExt))
|
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
|
||||||
{
|
{
|
||||||
_buildUrl = downloadURL;
|
_buildUrl = downloadURL;
|
||||||
|
|
||||||
|
@ -137,7 +137,6 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make process DPI aware for proper window sizing on high-res screens.
|
// Make process DPI aware for proper window sizing on high-res screens.
|
||||||
ForceDpiAware.Windows();
|
|
||||||
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
||||||
|
|
||||||
// Delete backup files after updating.
|
// Delete backup files after updating.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Version>1.0.0-dirty</Version>
|
<Version>1.0.0-dirty</Version>
|
||||||
|
@ -73,10 +73,13 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
{
|
{
|
||||||
SizeChanged?.Invoke(this, rect.Size);
|
SizeChanged?.Invoke(this, rect.Size);
|
||||||
|
|
||||||
RenderSize = rect.Size * Program.WindowScaleFactor;
|
if (!rect.IsEmpty)
|
||||||
|
{
|
||||||
|
RenderSize = rect.Size * VisualRoot.RenderScaling;
|
||||||
|
|
||||||
_glDrawOperation?.Dispose();
|
_glDrawOperation?.Dispose();
|
||||||
_glDrawOperation = new GlDrawOperation(this);
|
_glDrawOperation = new GlDrawOperation(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(DrawingContext context)
|
public override void Render(DrawingContext context)
|
||||||
|
@ -123,10 +123,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
CheckLaunchState();
|
CheckLaunchState();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
Program.WindowScaleFactor = this.PlatformImpl.RenderScaling;
|
|
||||||
}
|
|
||||||
_rendererWaitEvent = new AutoResetEvent(false);
|
_rendererWaitEvent = new AutoResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,17 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static long AlignUp(long value, int size)
|
public static long AlignUp(long value, int size)
|
||||||
{
|
{
|
||||||
return (value + (size - 1)) & -(long)size;
|
return AlignUp(value, (long)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong AlignUp(ulong value, ulong size)
|
||||||
|
{
|
||||||
|
return (ulong)AlignUp((long)value, (long)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long AlignUp(long value, long size)
|
||||||
|
{
|
||||||
|
return (value + (size - 1)) & -size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint AlignDown(uint value, int size)
|
public static uint AlignDown(uint value, int size)
|
||||||
@ -42,7 +52,17 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static long AlignDown(long value, int size)
|
public static long AlignDown(long value, int size)
|
||||||
{
|
{
|
||||||
return value & -(long)size;
|
return AlignDown(value, (long)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong AlignDown(ulong value, ulong size)
|
||||||
|
{
|
||||||
|
return (ulong)AlignDown((long)value, (long)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long AlignDown(long value, long size)
|
||||||
|
{
|
||||||
|
return value & -size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int DivRoundUp(int value, int dividend)
|
public static int DivRoundUp(int value, int dividend)
|
||||||
|
64
Ryujinx.Cpu/ExceptionCallbacks.cs
Normal file
64
Ryujinx.Cpu/ExceptionCallbacks.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
namespace Ryujinx.Cpu
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Exception callback without any additional arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context for the thread where the exception was triggered</param>
|
||||||
|
public delegate void ExceptionCallbackNoArgs(IExecutionContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception callback.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context for the thread where the exception was triggered</param>
|
||||||
|
/// <param name="address">Address of the instruction that caused the exception</param>
|
||||||
|
/// <param name="imm">Immediate value of the instruction that caused the exception, or for undefined instruction, the instruction itself</param>
|
||||||
|
public delegate void ExceptionCallback(IExecutionContext context, ulong address, int imm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores handlers for the various CPU exceptions.
|
||||||
|
/// </summary>
|
||||||
|
public struct ExceptionCallbacks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ExceptionCallbackNoArgs InterruptCallback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for CPU software interrupts caused by the Arm BRK instruction.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ExceptionCallback BreakCallback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for CPU software interrupts caused by the Arm SVC instruction.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ExceptionCallback SupervisorCallback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for CPU software interrupts caused by any undefined Arm instruction.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ExceptionCallback UndefinedCallback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new exception callbacks structure.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All handlers are optional, and if null, the CPU will just continue executing as if nothing happened.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="interruptCallback">Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/></param>
|
||||||
|
/// <param name="breakCallback">Handler for CPU software interrupts caused by the Arm BRK instruction</param>
|
||||||
|
/// <param name="supervisorCallback">Handler for CPU software interrupts caused by the Arm SVC instruction</param>
|
||||||
|
/// <param name="undefinedCallback">Handler for CPU software interrupts caused by any undefined Arm instruction</param>
|
||||||
|
public ExceptionCallbacks(
|
||||||
|
ExceptionCallbackNoArgs interruptCallback = null,
|
||||||
|
ExceptionCallback breakCallback = null,
|
||||||
|
ExceptionCallback supervisorCallback = null,
|
||||||
|
ExceptionCallback undefinedCallback = null)
|
||||||
|
{
|
||||||
|
InterruptCallback = interruptCallback;
|
||||||
|
BreakCallback = breakCallback;
|
||||||
|
SupervisorCallback = supervisorCallback;
|
||||||
|
UndefinedCallback = undefinedCallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Ryujinx.Cpu/ICpuContext.cs
Normal file
39
Ryujinx.Cpu/ICpuContext.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
namespace Ryujinx.Cpu
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CPU context interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICpuContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new execution context that will store thread CPU register state when executing guest code.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exceptionCallbacks">Optional functions to be called when the CPU receives an interrupt</param>
|
||||||
|
/// <returns>Execution context</returns>
|
||||||
|
IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts executing code at a specified entry point address.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This function only returns when the execution is stopped, by calling <see cref="IExecutionContext.StopRunning"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="context">Execution context to be used for this run</param>
|
||||||
|
/// <param name="address">Entry point address</param>
|
||||||
|
void Execute(IExecutionContext context, ulong address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalidates the instruction cache for a given memory region.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This should be called if code is modified to make the CPU emulator aware of the modifications,
|
||||||
|
/// otherwise it might run stale code which will lead to errors and crashes.
|
||||||
|
/// Calling this function is not necessary if the code memory was modified by guest code,
|
||||||
|
/// as the expectation is that it will do it on its own using the appropriate cache invalidation instructions,
|
||||||
|
/// except on Arm32 where those instructions can't be used in unprivileged mode.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="address">Address of the region to be invalidated</param>
|
||||||
|
/// <param name="size">Size of the region to be invalidated</param>
|
||||||
|
void InvalidateCacheRegion(ulong address, ulong size);
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Cpu/ICpuEngine.cs
Normal file
18
Ryujinx.Cpu/ICpuEngine.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using ARMeilleure.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CPU execution engine interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICpuEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new CPU context that can be used to run code for multiple threads sharing an address space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">Memory manager for the address space of the context</param>
|
||||||
|
/// <param name="for64Bit">Indicates if the context will be used to run 64-bit or 32-bit Arm code</param>
|
||||||
|
/// <returns>CPU context</returns>
|
||||||
|
ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit);
|
||||||
|
}
|
||||||
|
}
|
112
Ryujinx.Cpu/IExecutionContext.cs
Normal file
112
Ryujinx.Cpu/IExecutionContext.cs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
using ARMeilleure.State;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CPU register state interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IExecutionContext : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current Program Counter.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In some implementations, this value might not be accurate and might not point to the last instruction executed.
|
||||||
|
/// </remarks>
|
||||||
|
ulong Pc { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thread ID Register (EL0).
|
||||||
|
/// </summary>
|
||||||
|
long TpidrEl0 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thread ID Register (read-only) (EL0).
|
||||||
|
/// </summary>
|
||||||
|
long TpidrroEl0 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processor State register.
|
||||||
|
/// </summary>
|
||||||
|
uint Pstate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Floating-point Control Register.
|
||||||
|
/// </summary>
|
||||||
|
uint Fpcr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Floating-point Status Register.
|
||||||
|
/// </summary>
|
||||||
|
uint Fpsr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whenever the CPU is running 64-bit (AArch64 mode) or 32-bit (AArch32 mode) code.
|
||||||
|
/// </summary>
|
||||||
|
bool IsAarch32 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whenever the CPU is still running code.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Even if this is false, the guest code might be still exiting.
|
||||||
|
/// One must not assume that the code is no longer running from this property alone.
|
||||||
|
/// </remarks>
|
||||||
|
bool Running { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of a general purpose register.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The special <paramref name="index"/> of 31 can be used to access the SP (Stack Pointer) register.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
|
||||||
|
/// <returns>The register value</returns>
|
||||||
|
ulong GetX(int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the value of a general purpose register.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The special <paramref name="index"/> of 31 can be used to access the SP (Stack Pointer) register.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
|
||||||
|
/// <param name="value">Value to be set</param>
|
||||||
|
void SetX(int index, ulong value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of a FP/SIMD register.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
|
||||||
|
/// <returns>The register value</returns>
|
||||||
|
V128 GetV(int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the value of a FP/SIMD register.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the register, in the range 0-31 (inclusive)</param>
|
||||||
|
/// <param name="value">Value to be set</param>
|
||||||
|
void SetV(int index, V128 value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests the thread to stop running temporarily and call <see cref="ExceptionCallbacks.InterruptCallback"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The thread might not pause immediately.
|
||||||
|
/// One must not assume that guest code is no longer being executed by the thread after calling this function.
|
||||||
|
/// </remarks>
|
||||||
|
void RequestInterrupt();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests the thread to stop running guest code and return as soon as possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The thread might not stop immediately.
|
||||||
|
/// One must not assume that guest code is no longer being executed by the thread after calling this function.
|
||||||
|
/// After a thread has been stopped, it can't be restarted with the same <see cref="IExecutionContext"/>.
|
||||||
|
/// If you only need to pause the thread temporarily, use <see cref="RequestInterrupt"/> instead.
|
||||||
|
/// </remarks>
|
||||||
|
void StopRunning();
|
||||||
|
}
|
||||||
|
}
|
31
Ryujinx.Cpu/ITickSource.cs
Normal file
31
Ryujinx.Cpu/ITickSource.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using ARMeilleure.State;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tick source interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITickSource : ICounter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Time elapsed since the counter was created.
|
||||||
|
/// </summary>
|
||||||
|
TimeSpan ElapsedTime { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time elapsed since the counter was created, in seconds.
|
||||||
|
/// </summary>
|
||||||
|
double ElapsedSeconds { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops counting.
|
||||||
|
/// </summary>
|
||||||
|
void Suspend();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resumes counting after a call to <see cref="Suspend"/>.
|
||||||
|
/// </summary>
|
||||||
|
void Resume();
|
||||||
|
}
|
||||||
|
}
|
41
Ryujinx.Cpu/Jit/JitCpuContext.cs
Normal file
41
Ryujinx.Cpu/Jit/JitCpuContext.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using ARMeilleure.Memory;
|
||||||
|
using ARMeilleure.Translation;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu.Jit
|
||||||
|
{
|
||||||
|
class JitCpuContext : ICpuContext
|
||||||
|
{
|
||||||
|
private readonly ITickSource _tickSource;
|
||||||
|
private readonly Translator _translator;
|
||||||
|
|
||||||
|
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||||
|
{
|
||||||
|
_tickSource = tickSource;
|
||||||
|
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
||||||
|
memory.UnmapEvent += UnmapHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnmapHandler(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_translator.InvalidateJitCacheRegion(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||||
|
{
|
||||||
|
return new JitExecutionContext(new JitMemoryAllocator(), _tickSource, exceptionCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Execute(IExecutionContext context, ulong address)
|
||||||
|
{
|
||||||
|
_translator.Execute(((JitExecutionContext)context).Impl, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void InvalidateCacheRegion(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_translator.InvalidateJitCacheRegion(address, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Cpu/Jit/JitEngine.cs
Normal file
20
Ryujinx.Cpu/Jit/JitEngine.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using ARMeilleure.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu.Jit
|
||||||
|
{
|
||||||
|
public class JitEngine : ICpuEngine
|
||||||
|
{
|
||||||
|
private readonly ITickSource _tickSource;
|
||||||
|
|
||||||
|
public JitEngine(ITickSource tickSource)
|
||||||
|
{
|
||||||
|
_tickSource = tickSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
|
||||||
|
{
|
||||||
|
return new JitCpuContext(_tickSource, memoryManager, for64Bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
Ryujinx.Cpu/Jit/JitExecutionContext.cs
Normal file
123
Ryujinx.Cpu/Jit/JitExecutionContext.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
using ARMeilleure.Memory;
|
||||||
|
using ARMeilleure.State;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu.Jit
|
||||||
|
{
|
||||||
|
class JitExecutionContext : IExecutionContext
|
||||||
|
{
|
||||||
|
private readonly ExecutionContext _impl;
|
||||||
|
internal ExecutionContext Impl => _impl;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ulong Pc => _impl.Pc;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public long TpidrEl0
|
||||||
|
{
|
||||||
|
get => _impl.TpidrEl0;
|
||||||
|
set => _impl.TpidrEl0 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public long TpidrroEl0
|
||||||
|
{
|
||||||
|
get => _impl.TpidrroEl0;
|
||||||
|
set => _impl.TpidrroEl0 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Pstate
|
||||||
|
{
|
||||||
|
get => _impl.Pstate;
|
||||||
|
set => _impl.Pstate = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Fpcr
|
||||||
|
{
|
||||||
|
get => (uint)_impl.Fpcr;
|
||||||
|
set => _impl.Fpcr = (FPCR)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public uint Fpsr
|
||||||
|
{
|
||||||
|
get => (uint)_impl.Fpsr;
|
||||||
|
set => _impl.Fpsr = (FPSR)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsAarch32
|
||||||
|
{
|
||||||
|
get => _impl.IsAarch32;
|
||||||
|
set => _impl.IsAarch32 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Running => _impl.Running;
|
||||||
|
|
||||||
|
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||||
|
|
||||||
|
public JitExecutionContext(IJitMemoryAllocator allocator, ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||||
|
{
|
||||||
|
_impl = new ExecutionContext(
|
||||||
|
allocator,
|
||||||
|
counter,
|
||||||
|
InterruptHandler,
|
||||||
|
BreakHandler,
|
||||||
|
SupervisorCallHandler,
|
||||||
|
UndefinedHandler);
|
||||||
|
|
||||||
|
_exceptionCallbacks = exceptionCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ulong GetX(int index) => _impl.GetX(index);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetX(int index, ulong value) => _impl.SetX(index, value);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public V128 GetV(int index) => _impl.GetV(index);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetV(int index, V128 value) => _impl.SetV(index, value);
|
||||||
|
|
||||||
|
private void InterruptHandler(ExecutionContext context)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BreakHandler(ExecutionContext context, ulong address, int imm)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UndefinedHandler(ExecutionContext context, ulong address, int opCode)
|
||||||
|
{
|
||||||
|
_exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RequestInterrupt()
|
||||||
|
{
|
||||||
|
_impl.RequestInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void StopRunning()
|
||||||
|
{
|
||||||
|
_impl.StopRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_impl.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
class JitMemoryAllocator : IJitMemoryAllocator
|
public class JitMemoryAllocator : IJitMemoryAllocator
|
||||||
{
|
{
|
||||||
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
||||||
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve);
|
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve);
|
@ -2,9 +2,9 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
class JitMemoryBlock : IJitMemoryBlock
|
public class JitMemoryBlock : IJitMemoryBlock
|
||||||
{
|
{
|
||||||
private readonly MemoryBlock _impl;
|
private readonly MemoryBlock _impl;
|
||||||
|
|
@ -10,7 +10,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a CPU memory manager.
|
/// Represents a CPU memory manager.
|
@ -8,12 +8,12 @@ using System.Collections.Generic;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
public sealed class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||||
{
|
{
|
||||||
public const int PageBits = 12;
|
public const int PageBits = 12;
|
||||||
public const int PageSize = 1 << PageBits;
|
public const int PageSize = 1 << PageBits;
|
45
Ryujinx.Cpu/TickSource.cs
Normal file
45
Ryujinx.Cpu/TickSource.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu
|
||||||
|
{
|
||||||
|
public class TickSource : ITickSource
|
||||||
|
{
|
||||||
|
private static Stopwatch _tickCounter;
|
||||||
|
|
||||||
|
private static double _hostTickFreq;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ulong Frequency { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ulong Counter => (ulong)(ElapsedSeconds * Frequency);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public TimeSpan ElapsedTime => _tickCounter.Elapsed;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq;
|
||||||
|
|
||||||
|
public TickSource(ulong frequency)
|
||||||
|
{
|
||||||
|
Frequency = frequency;
|
||||||
|
_hostTickFreq = 1.0 / Stopwatch.Frequency;
|
||||||
|
|
||||||
|
_tickCounter = new Stopwatch();
|
||||||
|
_tickCounter.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Suspend()
|
||||||
|
{
|
||||||
|
_tickCounter.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Resume()
|
||||||
|
{
|
||||||
|
_tickCounter.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,10 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void ClearBuffer(BufferHandle destination, int offset, int size, uint value);
|
void ClearBuffer(BufferHandle destination, int offset, int size, uint value);
|
||||||
|
|
||||||
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color);
|
||||||
|
|
||||||
void ClearRenderTargetDepthStencil(
|
void ClearRenderTargetDepthStencil(
|
||||||
|
int layer,
|
||||||
float depthValue,
|
float depthValue,
|
||||||
bool depthMask,
|
bool depthMask,
|
||||||
int stencilValue,
|
int stencilValue,
|
||||||
|
@ -4,19 +4,21 @@
|
|||||||
{
|
{
|
||||||
public CommandType CommandType => CommandType.ClearRenderTargetColor;
|
public CommandType CommandType => CommandType.ClearRenderTargetColor;
|
||||||
private int _index;
|
private int _index;
|
||||||
|
private int _layer;
|
||||||
private uint _componentMask;
|
private uint _componentMask;
|
||||||
private ColorF _color;
|
private ColorF _color;
|
||||||
|
|
||||||
public void Set(int index, uint componentMask, ColorF color)
|
public void Set(int index, int layer, uint componentMask, ColorF color)
|
||||||
{
|
{
|
||||||
_index = index;
|
_index = index;
|
||||||
|
_layer = layer;
|
||||||
_componentMask = componentMask;
|
_componentMask = componentMask;
|
||||||
_color = color;
|
_color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
renderer.Pipeline.ClearRenderTargetColor(command._index, command._componentMask, command._color);
|
renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._componentMask, command._color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,15 @@
|
|||||||
struct ClearRenderTargetDepthStencilCommand : IGALCommand
|
struct ClearRenderTargetDepthStencilCommand : IGALCommand
|
||||||
{
|
{
|
||||||
public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil;
|
public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil;
|
||||||
|
private int _layer;
|
||||||
private float _depthValue;
|
private float _depthValue;
|
||||||
private bool _depthMask;
|
private bool _depthMask;
|
||||||
private int _stencilValue;
|
private int _stencilValue;
|
||||||
private int _stencilMask;
|
private int _stencilMask;
|
||||||
|
|
||||||
public void Set(float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
public void Set(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||||
{
|
{
|
||||||
|
_layer = layer;
|
||||||
_depthValue = depthValue;
|
_depthValue = depthValue;
|
||||||
_depthMask = depthMask;
|
_depthMask = depthMask;
|
||||||
_stencilValue = stencilValue;
|
_stencilValue = stencilValue;
|
||||||
@ -18,7 +20,7 @@
|
|||||||
|
|
||||||
public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
renderer.Pipeline.ClearRenderTargetDepthStencil(command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
|
renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,15 +40,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
|
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
|
||||||
{
|
{
|
||||||
_renderer.New<ClearRenderTargetColorCommand>().Set(index, componentMask, color);
|
_renderer.New<ClearRenderTargetColorCommand>().Set(index, layer, componentMask, color);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||||
{
|
{
|
||||||
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(depthValue, depthMask, stencilValue, stencilMask);
|
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, depthValue, depthMask, stencilValue, stencilMask);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
CubemapArray,
|
CubemapArray,
|
||||||
TextureBuffer
|
TextureBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class TargetExtensions
|
||||||
|
{
|
||||||
|
public static bool IsMultisample(this Target target)
|
||||||
|
{
|
||||||
|
return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -188,6 +188,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
_channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
|
_channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
|
||||||
_channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
|
_channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
|
||||||
|
|
||||||
|
int maxTextureBinding = -1;
|
||||||
|
int maxImageBinding = -1;
|
||||||
|
|
||||||
TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count);
|
TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count);
|
||||||
|
|
||||||
for (int index = 0; index < info.Textures.Count; index++)
|
for (int index = 0; index < info.Textures.Count; index++)
|
||||||
@ -202,6 +205,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
descriptor.CbufSlot,
|
descriptor.CbufSlot,
|
||||||
descriptor.HandleIndex,
|
descriptor.HandleIndex,
|
||||||
descriptor.Flags);
|
descriptor.Flags);
|
||||||
|
|
||||||
|
if (descriptor.Binding > maxTextureBinding)
|
||||||
|
{
|
||||||
|
maxTextureBinding = descriptor.Binding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count);
|
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count);
|
||||||
@ -220,9 +228,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
descriptor.CbufSlot,
|
descriptor.CbufSlot,
|
||||||
descriptor.HandleIndex,
|
descriptor.HandleIndex,
|
||||||
descriptor.Flags);
|
descriptor.Flags);
|
||||||
|
|
||||||
|
if (descriptor.Binding > maxImageBinding)
|
||||||
|
{
|
||||||
|
maxImageBinding = descriptor.Binding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_channel.TextureManager.CommitComputeBindings();
|
_channel.TextureManager.SetComputeMaxBindings(maxTextureBinding, maxImageBinding);
|
||||||
|
|
||||||
|
// Should never return false for mismatching spec state, since the shader was fetched above.
|
||||||
|
_channel.TextureManager.CommitComputeBindings(cs.SpecializationState);
|
||||||
|
|
||||||
_channel.BufferManager.CommitComputeBindings();
|
_channel.BufferManager.CommitComputeBindings();
|
||||||
|
|
||||||
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
|
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
|
||||||
|
@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
private bool _instancedDrawPending;
|
private bool _instancedDrawPending;
|
||||||
private bool _instancedIndexed;
|
private bool _instancedIndexed;
|
||||||
|
private bool _instancedIndexedInline;
|
||||||
|
|
||||||
private int _instancedFirstIndex;
|
private int _instancedFirstIndex;
|
||||||
private int _instancedFirstVertex;
|
private int _instancedFirstVertex;
|
||||||
@ -134,13 +135,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
_instancedDrawPending = true;
|
_instancedDrawPending = true;
|
||||||
|
|
||||||
|
int ibCount = _drawState.IbStreamer.InlineIndexCount;
|
||||||
|
|
||||||
_instancedIndexed = _drawState.DrawIndexed;
|
_instancedIndexed = _drawState.DrawIndexed;
|
||||||
|
_instancedIndexedInline = ibCount != 0;
|
||||||
|
|
||||||
_instancedFirstIndex = firstIndex;
|
_instancedFirstIndex = firstIndex;
|
||||||
_instancedFirstVertex = (int)_state.State.FirstVertex;
|
_instancedFirstVertex = (int)_state.State.FirstVertex;
|
||||||
_instancedFirstInstance = (int)_state.State.FirstInstance;
|
_instancedFirstInstance = (int)_state.State.FirstInstance;
|
||||||
|
|
||||||
_instancedIndexCount = indexCount;
|
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
|
||||||
|
|
||||||
var drawState = _state.State.VertexBufferDrawState;
|
var drawState = _state.State.VertexBufferDrawState;
|
||||||
|
|
||||||
@ -451,8 +455,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
_instancedDrawPending = false;
|
_instancedDrawPending = false;
|
||||||
|
|
||||||
if (_instancedIndexed)
|
bool indexedInline = _instancedIndexedInline;
|
||||||
|
|
||||||
|
if (_instancedIndexed || indexedInline)
|
||||||
{
|
{
|
||||||
|
if (indexedInline)
|
||||||
|
{
|
||||||
|
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
|
||||||
|
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
|
||||||
|
|
||||||
|
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
|
||||||
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.DrawIndexed(
|
_context.Renderer.Pipeline.DrawIndexed(
|
||||||
_instancedIndexCount,
|
_instancedIndexCount,
|
||||||
_instanceIndex + 1,
|
_instanceIndex + 1,
|
||||||
@ -491,8 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
}
|
}
|
||||||
|
|
||||||
int index = (argument >> 6) & 0xf;
|
int index = (argument >> 6) & 0xf;
|
||||||
|
int layer = (argument >> 10) & 0x3ff;
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: false, singleUse: index);
|
engine.UpdateRenderTargetState(useControl: false, layered: layer != 0, singleUse: index);
|
||||||
|
|
||||||
// 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
|
||||||
@ -567,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
|
ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
|
_context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clearDepth || clearStencil)
|
if (clearDepth || clearStencil)
|
||||||
@ -588,6 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
}
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
||||||
|
layer,
|
||||||
depthValue,
|
depthValue,
|
||||||
clearDepth,
|
clearDepth,
|
||||||
stencilValue,
|
stencilValue,
|
||||||
|
@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasInlineIndexData => _inlineIndexCount != 0;
|
public bool HasInlineIndexData => _inlineIndexCount != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total numbers of indices that have been pushed.
|
||||||
|
/// </summary>
|
||||||
|
public int InlineIndexCount => _inlineIndexCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle for the host buffer currently holding the inline index buffer data.
|
/// Gets the handle for the host buffer currently holding the inline index buffer data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private enum ReportCounterType
|
private enum ReportCounterType
|
||||||
{
|
{
|
||||||
Zero = 0,
|
Payload = 0,
|
||||||
InputVertices = 1,
|
InputVertices = 1,
|
||||||
InputPrimitives = 3,
|
InputPrimitives = 3,
|
||||||
VertexShaderInvocations = 5,
|
VertexShaderInvocations = 5,
|
||||||
@ -169,8 +169,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case ReportCounterType.Zero:
|
case ReportCounterType.Payload:
|
||||||
resultHandler(null, 0);
|
resultHandler(null, (ulong)_state.State.SemaphorePayload);
|
||||||
break;
|
break;
|
||||||
case ReportCounterType.SamplesPassed:
|
case ReportCounterType.SamplesPassed:
|
||||||
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false);
|
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false);
|
||||||
|
@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
// of the shader for the new state.
|
// of the shader for the new state.
|
||||||
if (_shaderSpecState != null)
|
if (_shaderSpecState != null)
|
||||||
{
|
{
|
||||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState()))
|
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false))
|
||||||
{
|
{
|
||||||
ForceShaderUpdate();
|
ForceShaderUpdate();
|
||||||
}
|
}
|
||||||
@ -275,7 +275,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
UpdateStorageBuffers();
|
UpdateStorageBuffers();
|
||||||
|
|
||||||
_channel.TextureManager.CommitGraphicsBindings();
|
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState))
|
||||||
|
{
|
||||||
|
// Shader must be reloaded.
|
||||||
|
UpdateShaderState();
|
||||||
|
}
|
||||||
|
|
||||||
_channel.BufferManager.CommitGraphicsBindings();
|
_channel.BufferManager.CommitGraphicsBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,8 +367,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// 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="useControl">Use draw buffers information from render target control register</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, int singleUse = -1)
|
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
var rtControl = _state.State.RtControl;
|
var rtControl = _state.State.RtControl;
|
||||||
@ -399,7 +405,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
colorState,
|
colorState,
|
||||||
_vtgWritesRtLayer,
|
_vtgWritesRtLayer || layered,
|
||||||
samplesInX,
|
samplesInX,
|
||||||
samplesInY,
|
samplesInY,
|
||||||
sizeHint);
|
sizeHint);
|
||||||
@ -433,6 +439,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
memoryManager,
|
memoryManager,
|
||||||
dsState,
|
dsState,
|
||||||
dsSize,
|
dsSize,
|
||||||
|
_vtgWritesRtLayer || layered,
|
||||||
samplesInX,
|
samplesInX,
|
||||||
samplesInY,
|
samplesInY,
|
||||||
sizeHint);
|
sizeHint);
|
||||||
@ -1148,6 +1155,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int maxTextureBinding = -1;
|
||||||
|
int maxImageBinding = -1;
|
||||||
|
|
||||||
Span<TextureBindingInfo> textureBindings = _channel.TextureManager.RentGraphicsTextureBindings(stage, info.Textures.Count);
|
Span<TextureBindingInfo> textureBindings = _channel.TextureManager.RentGraphicsTextureBindings(stage, info.Textures.Count);
|
||||||
|
|
||||||
if (info.UsesRtLayer)
|
if (info.UsesRtLayer)
|
||||||
@ -1167,6 +1177,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
descriptor.CbufSlot,
|
descriptor.CbufSlot,
|
||||||
descriptor.HandleIndex,
|
descriptor.HandleIndex,
|
||||||
descriptor.Flags);
|
descriptor.Flags);
|
||||||
|
|
||||||
|
if (descriptor.Binding > maxTextureBinding)
|
||||||
|
{
|
||||||
|
maxTextureBinding = descriptor.Binding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentGraphicsImageBindings(stage, info.Images.Count);
|
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentGraphicsImageBindings(stage, info.Images.Count);
|
||||||
@ -1185,8 +1200,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
descriptor.CbufSlot,
|
descriptor.CbufSlot,
|
||||||
descriptor.HandleIndex,
|
descriptor.HandleIndex,
|
||||||
descriptor.Flags);
|
descriptor.Flags);
|
||||||
|
|
||||||
|
if (descriptor.Binding > maxImageBinding)
|
||||||
|
{
|
||||||
|
maxImageBinding = descriptor.Binding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_channel.TextureManager.SetGraphicsMaxBindings(maxTextureBinding, maxImageBinding);
|
||||||
|
|
||||||
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
|
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
|
||||||
_channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
|
_channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
|
||||||
}
|
}
|
||||||
|
@ -131,10 +131,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// 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="useControl">Use draw buffers information from render target control register</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, int singleUse = -1)
|
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
||||||
{
|
{
|
||||||
_stateUpdater.UpdateRenderTargetState(useControl, singleUse);
|
_stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Cpu.Tracking;
|
using Ryujinx.Cpu.Tracking;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@ -16,6 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
protected GpuContext Context;
|
protected GpuContext Context;
|
||||||
protected PhysicalMemory PhysicalMemory;
|
protected PhysicalMemory PhysicalMemory;
|
||||||
protected int SequenceNumber;
|
protected int SequenceNumber;
|
||||||
|
protected int ModifiedSequenceNumber;
|
||||||
|
|
||||||
protected T1[] Items;
|
protected T1[] Items;
|
||||||
protected T2[] DescriptorCache;
|
protected T2[] DescriptorCache;
|
||||||
@ -41,6 +43,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly CpuMultiRegionHandle _memoryTracking;
|
private readonly CpuMultiRegionHandle _memoryTracking;
|
||||||
private readonly Action<ulong, ulong> _modifiedDelegate;
|
private readonly Action<ulong, ulong> _modifiedDelegate;
|
||||||
|
|
||||||
|
private int _modifiedSequenceOffset;
|
||||||
|
private bool _modified;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU resource pool.
|
/// Creates a new instance of the GPU resource pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -79,6 +84,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
|
return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a reference to the descriptor for a given ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
|
||||||
|
/// <returns>A reference to the descriptor</returns>
|
||||||
|
public ref readonly T2 GetDescriptorRef(int id)
|
||||||
|
{
|
||||||
|
return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(Address + (ulong)id * DescriptorSize, DescriptorSize))[0];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the GPU resource with the given ID.
|
/// Gets the GPU resource with the given ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -93,7 +108,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SynchronizeMemory()
|
public void SynchronizeMemory()
|
||||||
{
|
{
|
||||||
|
_modified = false;
|
||||||
_memoryTracking.QueryModified(_modifiedDelegate);
|
_memoryTracking.QueryModified(_modifiedDelegate);
|
||||||
|
|
||||||
|
if (_modified)
|
||||||
|
{
|
||||||
|
UpdateModifiedSequence();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -103,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="mSize">Size of the modified region</param>
|
/// <param name="mSize">Size of the modified region</param>
|
||||||
private void RegionModified(ulong mAddress, ulong mSize)
|
private void RegionModified(ulong mAddress, ulong mSize)
|
||||||
{
|
{
|
||||||
|
_modified = true;
|
||||||
|
|
||||||
if (mAddress < Address)
|
if (mAddress < Address)
|
||||||
{
|
{
|
||||||
mAddress = Address;
|
mAddress = Address;
|
||||||
@ -118,6 +141,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
InvalidateRangeImpl(mAddress, mSize);
|
InvalidateRangeImpl(mAddress, mSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the modified sequence number using the current sequence number and offset,
|
||||||
|
/// indicating that it has been modified.
|
||||||
|
/// </summary>
|
||||||
|
protected void UpdateModifiedSequence()
|
||||||
|
{
|
||||||
|
ModifiedSequenceNumber = SequenceNumber + _modifiedSequenceOffset;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An action to be performed when a precise memory access occurs to this resource.
|
/// An action to be performed when a precise memory access occurs to this resource.
|
||||||
/// Makes sure that the dirty flags are checked.
|
/// Makes sure that the dirty flags are checked.
|
||||||
@ -129,6 +161,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
if (write && Context.SequenceNumber == SequenceNumber)
|
if (write && Context.SequenceNumber == SequenceNumber)
|
||||||
{
|
{
|
||||||
|
if (ModifiedSequenceNumber == SequenceNumber + _modifiedSequenceOffset)
|
||||||
|
{
|
||||||
|
// The modified sequence number is offset when PreciseActions occur so that
|
||||||
|
// users checking it will see an increment and know the pool has changed since
|
||||||
|
// their last look, even though the main SequenceNumber has not been changed.
|
||||||
|
|
||||||
|
_modifiedSequenceOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force the pool to be checked again the next time it is used.
|
||||||
SequenceNumber--;
|
SequenceNumber--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class Sampler : IDisposable
|
class Sampler : IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// True if the sampler is disposed, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Host sampler object.
|
/// Host sampler object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -101,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
IsDisposed = true;
|
||||||
|
|
||||||
_hostSampler.Dispose();
|
_hostSampler.Dispose();
|
||||||
_anisoSampler?.Dispose();
|
_anisoSampler?.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Items[i] = null;
|
Items[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateModifiedSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
SequenceNumber = Context.SequenceNumber;
|
SequenceNumber = Context.SequenceNumber;
|
||||||
@ -71,6 +73,39 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return sampler;
|
return sampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A number that increments each time a modification is detected</returns>
|
||||||
|
public int CheckModified()
|
||||||
|
{
|
||||||
|
if (SequenceNumber != Context.SequenceNumber)
|
||||||
|
{
|
||||||
|
SequenceNumber = Context.SequenceNumber;
|
||||||
|
|
||||||
|
if (_forcedAnisotropy != GraphicsConfig.MaxAnisotropy)
|
||||||
|
{
|
||||||
|
_forcedAnisotropy = GraphicsConfig.MaxAnisotropy;
|
||||||
|
|
||||||
|
for (int i = 0; i < Items.Length; i++)
|
||||||
|
{
|
||||||
|
if (Items[i] != null)
|
||||||
|
{
|
||||||
|
Items[i].Dispose();
|
||||||
|
|
||||||
|
Items[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateModifiedSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizeMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModifiedSequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementation of the sampler pool range invalidation.
|
/// Implementation of the sampler pool range invalidation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -100,6 +100,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AlwaysFlushOnOverlap { get; private set; }
|
public bool AlwaysFlushOnOverlap { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments when the host texture is swapped, or when the texture is removed from all pools.
|
||||||
|
/// </summary>
|
||||||
|
public int InvalidatedSequence { get; private set; }
|
||||||
|
|
||||||
private int _depth;
|
private int _depth;
|
||||||
private int _layers;
|
private int _layers;
|
||||||
public int FirstLayer { get; private set; }
|
public int FirstLayer { get; private set; }
|
||||||
@ -1136,32 +1141,22 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="range">Texture view physical memory ranges</param>
|
/// <param name="range">Texture view physical memory ranges</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="allowMs">Indicates that multisample textures are allowed to match non-multisample requested textures</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, bool allowMs, out int firstLayer, out int firstLevel)
|
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
|
||||||
{
|
{
|
||||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
|
|
||||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
|
||||||
if (result != TextureViewCompatibility.Incompatible)
|
if (result != TextureViewCompatibility.Incompatible)
|
||||||
{
|
{
|
||||||
bool msTargetCompatible = false;
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
|
||||||
|
|
||||||
if (allowMs)
|
bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
|
||||||
|
if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
|
||||||
{
|
{
|
||||||
msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
|
result = TextureViewCompatibility.Incompatible;
|
||||||
}
|
|
||||||
|
|
||||||
if (!msTargetCompatible)
|
|
||||||
{
|
|
||||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
|
|
||||||
|
|
||||||
if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)
|
|
||||||
{
|
|
||||||
result = TextureViewCompatibility.Incompatible;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
|
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
|
||||||
@ -1417,6 +1412,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
DisposeTextures();
|
DisposeTextures();
|
||||||
|
|
||||||
HostTexture = hostTexture;
|
HostTexture = hostTexture;
|
||||||
|
InvalidatedSequence++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1545,6 +1541,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_poolOwners.Clear();
|
_poolOwners.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InvalidatedSequence++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@ -31,21 +35,30 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly TextureBindingInfo[][] _textureBindings;
|
private readonly TextureBindingInfo[][] _textureBindings;
|
||||||
private readonly TextureBindingInfo[][] _imageBindings;
|
private readonly TextureBindingInfo[][] _imageBindings;
|
||||||
|
|
||||||
private struct TextureStatePerStage
|
private struct TextureState
|
||||||
{
|
{
|
||||||
public ITexture Texture;
|
public ITexture Texture;
|
||||||
public ISampler Sampler;
|
public ISampler Sampler;
|
||||||
|
|
||||||
|
public int TextureHandle;
|
||||||
|
public int SamplerHandle;
|
||||||
|
public int InvalidatedSequence;
|
||||||
|
public Texture CachedTexture;
|
||||||
|
public Sampler CachedSampler;
|
||||||
|
public int ScaleIndex;
|
||||||
|
public TextureUsageFlags UsageFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly TextureStatePerStage[][] _textureState;
|
private TextureState[] _textureState;
|
||||||
private readonly TextureStatePerStage[][] _imageState;
|
private TextureState[] _imageState;
|
||||||
|
|
||||||
private int[] _textureBindingsCount;
|
private int[] _textureBindingsCount;
|
||||||
private int[] _imageBindingsCount;
|
private int[] _imageBindingsCount;
|
||||||
|
|
||||||
private int _textureBufferIndex;
|
private int _texturePoolSequence;
|
||||||
|
private int _samplerPoolSequence;
|
||||||
|
|
||||||
private bool _rebind;
|
private int _textureBufferIndex;
|
||||||
|
|
||||||
private readonly float[] _scales;
|
private readonly float[] _scales;
|
||||||
private bool _scaleChanged;
|
private bool _scaleChanged;
|
||||||
@ -72,8 +85,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_textureBindings = new TextureBindingInfo[stages][];
|
_textureBindings = new TextureBindingInfo[stages][];
|
||||||
_imageBindings = new TextureBindingInfo[stages][];
|
_imageBindings = new TextureBindingInfo[stages][];
|
||||||
|
|
||||||
_textureState = new TextureStatePerStage[stages][];
|
_textureState = new TextureState[InitialTextureStateSize];
|
||||||
_imageState = new TextureStatePerStage[stages][];
|
_imageState = new TextureState[InitialImageStateSize];
|
||||||
|
|
||||||
_textureBindingsCount = new int[stages];
|
_textureBindingsCount = new int[stages];
|
||||||
_imageBindingsCount = new int[stages];
|
_imageBindingsCount = new int[stages];
|
||||||
@ -82,9 +95,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
_textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
|
_textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
|
||||||
_imageBindings[stage] = new TextureBindingInfo[InitialImageStateSize];
|
_imageBindings[stage] = new TextureBindingInfo[InitialImageStateSize];
|
||||||
|
|
||||||
_textureState[stage] = new TextureStatePerStage[InitialTextureStateSize];
|
|
||||||
_imageState[stage] = new TextureStatePerStage[InitialImageStateSize];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,15 +109,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (count > _textureBindings[stage].Length)
|
if (count > _textureBindings[stage].Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _textureBindings[stage], count);
|
Array.Resize(ref _textureBindings[stage], count);
|
||||||
Array.Resize(ref _textureState[stage], count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int toClear = Math.Max(_textureBindingsCount[stage], count);
|
|
||||||
TextureStatePerStage[] state = _textureState[stage];
|
|
||||||
|
|
||||||
for (int i = 0; i < toClear; i++)
|
|
||||||
{
|
|
||||||
state[i] = new TextureStatePerStage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_textureBindingsCount[stage] = count;
|
_textureBindingsCount[stage] = count;
|
||||||
@ -126,15 +127,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (count > _imageBindings[stage].Length)
|
if (count > _imageBindings[stage].Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _imageBindings[stage], count);
|
Array.Resize(ref _imageBindings[stage], count);
|
||||||
Array.Resize(ref _imageState[stage], count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int toClear = Math.Max(_imageBindingsCount[stage], count);
|
|
||||||
TextureStatePerStage[] state = _imageState[stage];
|
|
||||||
|
|
||||||
for (int i = 0; i < toClear; i++)
|
|
||||||
{
|
|
||||||
state[i] = new TextureStatePerStage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_imageBindingsCount[stage] = count;
|
_imageBindingsCount[stage] = count;
|
||||||
@ -142,6 +134,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return _imageBindings[stage];
|
return _imageBindings[stage];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the max binding indexes for textures and images.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxTextureBinding">The maximum texture binding</param>
|
||||||
|
/// <param name="maxImageBinding">The maximum image binding</param>
|
||||||
|
public void SetMaxBindings(int maxTextureBinding, int maxImageBinding)
|
||||||
|
{
|
||||||
|
if (maxTextureBinding >= _textureState.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _textureState, maxTextureBinding + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxImageBinding >= _imageState.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _imageState, maxImageBinding + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the textures constant buffer index.
|
/// Sets the textures constant buffer index.
|
||||||
/// The constant buffer specified holds the texture handles.
|
/// The constant buffer specified holds the texture handles.
|
||||||
@ -222,18 +232,18 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Updates the texture scale for a given texture or image.
|
/// Updates the texture scale for a given texture or image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="texture">Start GPU virtual address of the pool</param>
|
/// <param name="texture">Start GPU virtual address of the pool</param>
|
||||||
/// <param name="binding">The related texture binding</param>
|
/// <param name="usageFlags">The related texture usage flags</param>
|
||||||
/// <param name="index">The texture/image binding index</param>
|
/// <param name="index">The texture/image binding index</param>
|
||||||
/// <param name="stage">The active shader stage</param>
|
/// <param name="stage">The active shader stage</param>
|
||||||
/// <returns>True if the given texture has become blacklisted, indicating that its host texture may have changed.</returns>
|
/// <returns>True if the given texture has become blacklisted, indicating that its host texture may have changed.</returns>
|
||||||
private bool UpdateScale(Texture texture, TextureBindingInfo binding, int index, ShaderStage stage)
|
private bool UpdateScale(Texture texture, TextureUsageFlags usageFlags, int index, ShaderStage stage)
|
||||||
{
|
{
|
||||||
float result = 1f;
|
float result = 1f;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
if ((binding.Flags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null)
|
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null)
|
||||||
{
|
{
|
||||||
if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
|
if ((usageFlags & TextureUsageFlags.ResScaleUnsupported) != 0)
|
||||||
{
|
{
|
||||||
changed = texture.ScaleMode != TextureScaleMode.Blacklisted;
|
changed = texture.ScaleMode != TextureScaleMode.Blacklisted;
|
||||||
texture.BlacklistScale();
|
texture.BlacklistScale();
|
||||||
@ -323,7 +333,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Ensures that the bindings are visible to the host GPU.
|
/// Ensures that the bindings are visible to the host GPU.
|
||||||
/// Note: this actually performs the binding using the host graphics API.
|
/// Note: this actually performs the binding using the host graphics API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CommitBindings()
|
/// <param name="specState">Specialization state for the bound shader</param>
|
||||||
|
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
||||||
|
public bool CommitBindings(ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
ulong texturePoolAddress = _texturePoolAddress;
|
ulong texturePoolAddress = _texturePoolAddress;
|
||||||
|
|
||||||
@ -331,10 +343,38 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
// Check if the texture pool has been modified since bindings were last committed.
|
||||||
|
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
||||||
|
bool poolModified = false;
|
||||||
|
|
||||||
|
if (texturePool != null)
|
||||||
|
{
|
||||||
|
int texturePoolSequence = texturePool.CheckModified();
|
||||||
|
|
||||||
|
if (_texturePoolSequence != texturePoolSequence)
|
||||||
|
{
|
||||||
|
poolModified = true;
|
||||||
|
_texturePoolSequence = texturePoolSequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_samplerPool != null)
|
||||||
|
{
|
||||||
|
int samplerPoolSequence = _samplerPool.CheckModified();
|
||||||
|
|
||||||
|
if (_samplerPoolSequence != samplerPoolSequence)
|
||||||
|
{
|
||||||
|
poolModified = true;
|
||||||
|
_samplerPoolSequence = samplerPoolSequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool specStateMatches = true;
|
||||||
|
|
||||||
if (_isCompute)
|
if (_isCompute)
|
||||||
{
|
{
|
||||||
CommitTextureBindings(texturePool, ShaderStage.Compute, 0);
|
specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
||||||
CommitImageBindings (texturePool, ShaderStage.Compute, 0);
|
specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -342,14 +382,57 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
int stageIndex = (int)stage - 1;
|
int stageIndex = (int)stage - 1;
|
||||||
|
|
||||||
CommitTextureBindings(texturePool, stage, stageIndex);
|
specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState);
|
||||||
CommitImageBindings (texturePool, stage, stageIndex);
|
specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitRenderScale();
|
CommitRenderScale();
|
||||||
|
|
||||||
_rebind = false;
|
return specStateMatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetch the constant buffers used for a texture to cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stageIndex">Stage index of the constant buffer</param>
|
||||||
|
/// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param>
|
||||||
|
/// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param>
|
||||||
|
/// <param name="cachedTextureBuffer">The currently cached texture buffer data</param>
|
||||||
|
/// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param>
|
||||||
|
/// <param name="textureBufferIndex">The new texture buffer index</param>
|
||||||
|
/// <param name="samplerBufferIndex">The new sampler buffer index</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void UpdateCachedBuffer(
|
||||||
|
int stageIndex,
|
||||||
|
ref int cachedTextureBufferIndex,
|
||||||
|
ref int cachedSamplerBufferIndex,
|
||||||
|
ref ReadOnlySpan<int> cachedTextureBuffer,
|
||||||
|
ref ReadOnlySpan<int> cachedSamplerBuffer,
|
||||||
|
int textureBufferIndex,
|
||||||
|
int samplerBufferIndex)
|
||||||
|
{
|
||||||
|
if (textureBufferIndex != cachedTextureBufferIndex)
|
||||||
|
{
|
||||||
|
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
|
||||||
|
cachedTextureBufferIndex = textureBufferIndex;
|
||||||
|
|
||||||
|
if (samplerBufferIndex == textureBufferIndex)
|
||||||
|
{
|
||||||
|
cachedSamplerBuffer = cachedTextureBuffer;
|
||||||
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (samplerBufferIndex != cachedSamplerBufferIndex)
|
||||||
|
{
|
||||||
|
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
|
||||||
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -358,13 +441,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pool">The current texture pool</param>
|
/// <param name="pool">The current texture pool</param>
|
||||||
/// <param name="stage">The shader stage using the textures to be bound</param>
|
/// <param name="stage">The shader stage using the textures to be bound</param>
|
||||||
/// <param name="stageIndex">The stage number of the specified shader stage</param>
|
/// <param name="stageIndex">The stage number of the specified shader stage</param
|
||||||
private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex)
|
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
|
||||||
|
/// <param name="specState">Specialization state for the bound shader</param>
|
||||||
|
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
|
||||||
|
private bool CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
int textureCount = _textureBindingsCount[stageIndex];
|
int textureCount = _textureBindingsCount[stageIndex];
|
||||||
if (textureCount == 0)
|
if (textureCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var samplerPool = _samplerPool;
|
var samplerPool = _samplerPool;
|
||||||
@ -372,17 +458,27 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (pool == null)
|
if (pool == null)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
|
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool specStateMatches = true;
|
||||||
|
|
||||||
|
int cachedTextureBufferIndex = -1;
|
||||||
|
int cachedSamplerBufferIndex = -1;
|
||||||
|
ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty;
|
||||||
|
ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty;
|
||||||
|
|
||||||
for (int index = 0; index < textureCount; index++)
|
for (int index = 0; index < textureCount; index++)
|
||||||
{
|
{
|
||||||
TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index];
|
TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index];
|
||||||
|
TextureUsageFlags usageFlags = bindingInfo.Flags;
|
||||||
|
|
||||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
|
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
|
||||||
|
|
||||||
int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
|
UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
|
||||||
int textureId = UnpackTextureId(packedId);
|
|
||||||
|
int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer);
|
||||||
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
int samplerId;
|
int samplerId;
|
||||||
|
|
||||||
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
|
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
|
||||||
@ -391,10 +487,42 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
samplerId = UnpackSamplerId(packedId);
|
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture texture = pool.Get(textureId);
|
ref TextureState state = ref _textureState[bindingInfo.Binding];
|
||||||
|
|
||||||
|
if (!poolModified &&
|
||||||
|
state.TextureHandle == textureId &&
|
||||||
|
state.SamplerHandle == samplerId &&
|
||||||
|
state.CachedTexture != null &&
|
||||||
|
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence &&
|
||||||
|
state.CachedSampler?.IsDisposed != true)
|
||||||
|
{
|
||||||
|
// The texture is already bound.
|
||||||
|
state.CachedTexture.SynchronizeMemory();
|
||||||
|
|
||||||
|
if ((state.ScaleIndex != index || state.UsageFlags != usageFlags) &&
|
||||||
|
UpdateScale(state.CachedTexture, usageFlags, index, stage))
|
||||||
|
{
|
||||||
|
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||||
|
|
||||||
|
state.Texture = hostTextureRebind;
|
||||||
|
state.ScaleIndex = index;
|
||||||
|
state.UsageFlags = usageFlags;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTextureRebind);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.TextureHandle = textureId;
|
||||||
|
state.SamplerHandle = samplerId;
|
||||||
|
|
||||||
|
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
|
||||||
|
|
||||||
|
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
||||||
|
|
||||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
|
|
||||||
@ -407,30 +535,38 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
|
if (state.Texture != hostTexture)
|
||||||
{
|
{
|
||||||
if (UpdateScale(texture, bindingInfo, index, stage))
|
if (UpdateScale(texture, usageFlags, index, stage))
|
||||||
{
|
{
|
||||||
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
}
|
}
|
||||||
|
|
||||||
_textureState[stageIndex][index].Texture = hostTexture;
|
state.Texture = hostTexture;
|
||||||
|
state.ScaleIndex = index;
|
||||||
|
state.UsageFlags = usageFlags;
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
|
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sampler sampler = samplerPool?.Get(samplerId);
|
Sampler sampler = samplerPool?.Get(samplerId);
|
||||||
|
state.CachedSampler = sampler;
|
||||||
|
|
||||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||||
|
|
||||||
if (_textureState[stageIndex][index].Sampler != hostSampler || _rebind)
|
if (state.Sampler != hostSampler)
|
||||||
{
|
{
|
||||||
_textureState[stageIndex][index].Sampler = hostSampler;
|
state.Sampler = hostSampler;
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
|
_context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.CachedTexture = texture;
|
||||||
|
state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return specStateMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -440,38 +576,90 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="pool">The current texture pool</param>
|
/// <param name="pool">The current texture pool</param>
|
||||||
/// <param name="stage">The shader stage using the textures to be bound</param>
|
/// <param name="stage">The shader stage using the textures to be bound</param>
|
||||||
/// <param name="stageIndex">The stage number of the specified shader stage</param>
|
/// <param name="stageIndex">The stage number of the specified shader stage</param>
|
||||||
private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex)
|
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
|
||||||
|
/// <param name="specState">Specialization state for the bound shader</param>
|
||||||
|
/// <returns>True if all bound images match the current shader specialiation state, false otherwise</returns>
|
||||||
|
private bool CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
int imageCount = _imageBindingsCount[stageIndex];
|
int imageCount = _imageBindingsCount[stageIndex];
|
||||||
if (imageCount == 0)
|
if (imageCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pool == null)
|
if (pool == null)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set.");
|
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set.");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scales for images appear after the texture ones.
|
// Scales for images appear after the texture ones.
|
||||||
int baseScaleIndex = _textureBindingsCount[stageIndex];
|
int baseScaleIndex = _textureBindingsCount[stageIndex];
|
||||||
|
|
||||||
|
int cachedTextureBufferIndex = -1;
|
||||||
|
int cachedSamplerBufferIndex = -1;
|
||||||
|
ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty;
|
||||||
|
ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty;
|
||||||
|
|
||||||
|
bool specStateMatches = true;
|
||||||
|
|
||||||
for (int index = 0; index < imageCount; index++)
|
for (int index = 0; index < imageCount; index++)
|
||||||
{
|
{
|
||||||
TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index];
|
TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index];
|
||||||
|
TextureUsageFlags usageFlags = bindingInfo.Flags;
|
||||||
|
int scaleIndex = baseScaleIndex + index;
|
||||||
|
|
||||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
|
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
|
||||||
|
|
||||||
int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
|
UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
|
||||||
int textureId = UnpackTextureId(packedId);
|
|
||||||
|
|
||||||
Texture texture = pool.Get(textureId);
|
int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer);
|
||||||
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
|
|
||||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
ref TextureState state = ref _imageState[bindingInfo.Binding];
|
||||||
|
|
||||||
bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
||||||
|
|
||||||
|
if (!poolModified &&
|
||||||
|
state.TextureHandle == textureId &&
|
||||||
|
state.CachedTexture != null &&
|
||||||
|
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
|
||||||
|
{
|
||||||
|
Texture cachedTexture = state.CachedTexture;
|
||||||
|
|
||||||
|
// The texture is already bound.
|
||||||
|
cachedTexture.SynchronizeMemory();
|
||||||
|
|
||||||
|
if (isStore)
|
||||||
|
{
|
||||||
|
cachedTexture?.SignalModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((state.ScaleIndex != index || state.UsageFlags != usageFlags) &&
|
||||||
|
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
|
||||||
|
{
|
||||||
|
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||||
|
|
||||||
|
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
||||||
|
|
||||||
|
state.Texture = hostTextureRebind;
|
||||||
|
state.ScaleIndex = scaleIndex;
|
||||||
|
state.UsageFlags = usageFlags;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.TextureHandle = textureId;
|
||||||
|
|
||||||
|
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
|
||||||
|
|
||||||
|
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
||||||
|
|
||||||
|
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
@ -494,14 +682,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
texture?.SignalModified();
|
texture?.SignalModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
|
if (state.Texture != hostTexture)
|
||||||
{
|
{
|
||||||
if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage))
|
if (UpdateScale(texture, usageFlags, scaleIndex, stage))
|
||||||
{
|
{
|
||||||
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
}
|
}
|
||||||
|
|
||||||
_imageState[stageIndex][index].Texture = hostTexture;
|
state.Texture = hostTexture;
|
||||||
|
state.ScaleIndex = scaleIndex;
|
||||||
|
state.UsageFlags = usageFlags;
|
||||||
|
|
||||||
Format format = bindingInfo.Format;
|
Format format = bindingInfo.Format;
|
||||||
|
|
||||||
@ -512,8 +702,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
|
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.CachedTexture = texture;
|
||||||
|
state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return specStateMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -537,7 +732,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(cbufSlot, bufferIndex);
|
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(cbufSlot, bufferIndex);
|
||||||
|
|
||||||
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
||||||
int textureId = UnpackTextureId(packedId);
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
|
|
||||||
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
||||||
|
|
||||||
@ -555,6 +750,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="textureBufferIndex">Index of the constant buffer holding the texture handles</param>
|
/// <param name="textureBufferIndex">Index of the constant buffer holding the texture handles</param>
|
||||||
/// <param name="samplerBufferIndex">Index of the constant buffer holding the sampler handles</param>
|
/// <param name="samplerBufferIndex">Index of the constant buffer holding the sampler handles</param>
|
||||||
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
|
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
|
private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
|
||||||
{
|
{
|
||||||
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
||||||
@ -590,32 +786,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks the texture ID from the real texture handle.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packedId">The real texture handle</param>
|
|
||||||
/// <returns>The texture ID</returns>
|
|
||||||
private static int UnpackTextureId(int packedId)
|
|
||||||
{
|
|
||||||
return (packedId >> 0) & 0xfffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks the sampler ID from the real texture handle.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packedId">The real texture handle</param>
|
|
||||||
/// <returns>The sampler ID</returns>
|
|
||||||
private static int UnpackSamplerId(int packedId)
|
|
||||||
{
|
|
||||||
return (packedId >> 20) & 0xfff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Rebind()
|
public void Rebind()
|
||||||
{
|
{
|
||||||
_rebind = true;
|
Array.Clear(_textureState);
|
||||||
|
Array.Clear(_imageState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -349,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
|
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
|
||||||
/// <param name="size">Size of the depth-stencil texture</param>
|
/// <param name="size">Size of the depth-stencil texture</param>
|
||||||
|
/// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
|
||||||
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
||||||
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
||||||
@ -357,6 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
MemoryManager memoryManager,
|
MemoryManager memoryManager,
|
||||||
RtDepthStencilState dsState,
|
RtDepthStencilState dsState,
|
||||||
Size3D size,
|
Size3D size,
|
||||||
|
bool layered,
|
||||||
int samplesInX,
|
int samplesInX,
|
||||||
int samplesInY,
|
int samplesInY,
|
||||||
Size sizeHint)
|
Size sizeHint)
|
||||||
@ -364,9 +366,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
Target target = (samplesInX | samplesInY) != 1
|
Target target;
|
||||||
? Target.Texture2DMultisample
|
|
||||||
: Target.Texture2D;
|
if (dsState.MemoryLayout.UnpackIsTarget3D())
|
||||||
|
{
|
||||||
|
target = Target.Texture3D;
|
||||||
|
}
|
||||||
|
else if ((samplesInX | samplesInY) != 1)
|
||||||
|
{
|
||||||
|
target = size.Depth > 1 && layered
|
||||||
|
? Target.Texture2DMultisampleArray
|
||||||
|
: Target.Texture2DMultisample;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = size.Depth > 1 && layered
|
||||||
|
? Target.Texture2DArray
|
||||||
|
: Target.Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
FormatInfo formatInfo = dsState.Format.Convert();
|
FormatInfo formatInfo = dsState.Format.Convert();
|
||||||
|
|
||||||
@ -547,7 +564,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
range.Value,
|
range.Value,
|
||||||
sizeInfo.LayerSize,
|
sizeInfo.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
flags.HasFlag(TextureSearchFlags.ForCopy),
|
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
out int firstLevel);
|
out int firstLevel);
|
||||||
|
|
||||||
@ -662,7 +678,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
overlap.Range,
|
overlap.Range,
|
||||||
overlap.LayerSize,
|
overlap.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
false,
|
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
out int firstLevel);
|
out int firstLevel);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
case Target.Texture2DMultisample:
|
case Target.Texture2DMultisample:
|
||||||
case Target.Texture2DMultisampleArray:
|
case Target.Texture2DMultisampleArray:
|
||||||
|
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
|
||||||
|
{
|
||||||
|
return TextureViewCompatibility.CopyOnly;
|
||||||
|
}
|
||||||
|
|
||||||
result = rhs.Target == Target.Texture2DMultisample ||
|
result = rhs.Target == Target.Texture2DMultisample ||
|
||||||
rhs.Target == Target.Texture2DMultisampleArray;
|
rhs.Target == Target.Texture2DMultisampleArray;
|
||||||
break;
|
break;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
@ -10,9 +11,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
class TextureManager : IDisposable
|
class TextureManager : IDisposable
|
||||||
{
|
{
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly GpuChannel _channel;
|
||||||
|
|
||||||
private readonly TextureBindingsManager _cpBindingsManager;
|
private readonly TextureBindingsManager _cpBindingsManager;
|
||||||
private readonly TextureBindingsManager _gpBindingsManager;
|
private readonly TextureBindingsManager _gpBindingsManager;
|
||||||
|
private readonly TexturePoolCache _texturePoolCache;
|
||||||
|
|
||||||
private readonly Texture[] _rtColors;
|
private readonly Texture[] _rtColors;
|
||||||
private readonly ITexture[] _rtHostColors;
|
private readonly ITexture[] _rtHostColors;
|
||||||
@ -35,6 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public TextureManager(GpuContext context, GpuChannel channel)
|
public TextureManager(GpuContext context, GpuChannel channel)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_channel = channel;
|
||||||
|
|
||||||
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
|
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
|
||||||
|
|
||||||
@ -43,6 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true);
|
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true);
|
||||||
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false);
|
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false);
|
||||||
|
_texturePoolCache = texturePoolCache;
|
||||||
|
|
||||||
_rtColors = new Texture[Constants.TotalRenderTargets];
|
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||||
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||||
@ -99,6 +104,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_cpBindingsManager.SetTextureBufferIndex(index);
|
_cpBindingsManager.SetTextureBufferIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the max binding indexes on the compute pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxTextureBinding">The maximum texture binding</param>
|
||||||
|
/// <param name="maxImageBinding">The maximum image binding</param>
|
||||||
|
public void SetComputeMaxBindings(int maxTextureBinding, int maxImageBinding)
|
||||||
|
{
|
||||||
|
_cpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture constant buffer index on the graphics pipeline.
|
/// Sets the texture constant buffer index on the graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -108,6 +123,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_gpBindingsManager.SetTextureBufferIndex(index);
|
_gpBindingsManager.SetTextureBufferIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the max binding indexes on the graphics pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxTextureBinding">The maximum texture binding</param>
|
||||||
|
/// <param name="maxImageBinding">The maximum image binding</param>
|
||||||
|
public void SetGraphicsMaxBindings(int maxTextureBinding, int maxImageBinding)
|
||||||
|
{
|
||||||
|
_gpBindingsManager.SetMaxBindings(maxTextureBinding, maxImageBinding);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the current sampler pool on the compute pipeline.
|
/// Sets the current sampler pool on the compute pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -335,25 +360,48 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Commits bindings on the compute pipeline.
|
/// Commits bindings on the compute pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CommitComputeBindings()
|
/// <param name="specState">Specialization state for the bound shader</param>
|
||||||
|
/// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
|
||||||
|
public bool CommitComputeBindings(ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
// Every time we switch between graphics and compute work,
|
// Every time we switch between graphics and compute work,
|
||||||
// we must rebind everything.
|
// we must rebind everything.
|
||||||
// Since compute work happens less often, we always do that
|
// Since compute work happens less often, we always do that
|
||||||
// before and after the compute dispatch.
|
// before and after the compute dispatch.
|
||||||
_cpBindingsManager.Rebind();
|
_cpBindingsManager.Rebind();
|
||||||
_cpBindingsManager.CommitBindings();
|
bool result = _cpBindingsManager.CommitBindings(specState);
|
||||||
_gpBindingsManager.Rebind();
|
_gpBindingsManager.Rebind();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Commits bindings on the graphics pipeline.
|
/// Commits bindings on the graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CommitGraphicsBindings()
|
/// <param name="specState">Specialization state for the bound shader</param>
|
||||||
|
/// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
|
||||||
|
public bool CommitGraphicsBindings(ShaderSpecializationState specState)
|
||||||
{
|
{
|
||||||
_gpBindingsManager.CommitBindings();
|
bool result = _gpBindingsManager.CommitBindings(specState);
|
||||||
|
|
||||||
UpdateRenderTargets();
|
UpdateRenderTargets();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a texture pool from the cache, with the given address and maximum id.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
|
||||||
|
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
||||||
|
/// <returns>The texture pool</returns>
|
||||||
|
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
|
||||||
|
{
|
||||||
|
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
||||||
|
|
||||||
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
|
||||||
|
|
||||||
|
return texturePool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
||||||
|
private TextureDescriptor _defaultDescriptor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Intrusive linked list node used on the texture pool cache.
|
/// Intrusive linked list node used on the texture pool cache.
|
||||||
@ -32,6 +33,62 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_channel = channel;
|
_channel = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the texture descripor and texture with the given ID with no bounds check or synchronization.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
|
/// <param name="texture">The texture with the given ID</param>
|
||||||
|
/// <returns>The texture descriptor with the given ID</returns>
|
||||||
|
private ref readonly TextureDescriptor GetInternal(int id, out Texture texture)
|
||||||
|
{
|
||||||
|
texture = Items[id];
|
||||||
|
|
||||||
|
ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(id);
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
|
|
||||||
|
ProcessDereferenceQueue();
|
||||||
|
|
||||||
|
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
|
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
return ref descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount(this, id);
|
||||||
|
|
||||||
|
Items[id] = texture;
|
||||||
|
|
||||||
|
DescriptorCache[id] = descriptor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (texture.ChangedSize)
|
||||||
|
{
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the texture with the given ID.
|
/// Gets the texture with the given ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -51,56 +108,49 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
SynchronizeMemory();
|
SynchronizeMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture texture = Items[id];
|
GetInternal(id, out Texture texture);
|
||||||
|
|
||||||
if (texture == null)
|
|
||||||
{
|
|
||||||
TextureDescriptor descriptor = GetDescriptor(id);
|
|
||||||
|
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
|
||||||
|
|
||||||
ProcessDereferenceQueue();
|
|
||||||
|
|
||||||
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
|
||||||
|
|
||||||
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
|
||||||
if (texture == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
texture.IncrementReferenceCount(this, id);
|
|
||||||
|
|
||||||
Items[id] = texture;
|
|
||||||
|
|
||||||
DescriptorCache[id] = descriptor;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (texture.ChangedSize)
|
|
||||||
{
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
TextureDescriptor descriptor = GetDescriptor(id);
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the texture descriptor and texture with the given ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method assumes that the pool has been manually synchronized before doing binding.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
|
/// <param name="texture">The texture with the given ID</param>
|
||||||
|
/// <returns>The texture descriptor with the given ID</returns>
|
||||||
|
public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture)
|
||||||
|
{
|
||||||
|
if ((uint)id >= Items.Length)
|
||||||
|
{
|
||||||
|
texture = null;
|
||||||
|
return ref _defaultDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When getting for binding, assume the pool has already been synchronized.
|
||||||
|
|
||||||
|
return ref GetInternal(id, out texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A number that increments each time a modification is detected</returns>
|
||||||
|
public int CheckModified()
|
||||||
|
{
|
||||||
|
if (SequenceNumber != Context.SequenceNumber)
|
||||||
|
{
|
||||||
|
SequenceNumber = Context.SequenceNumber;
|
||||||
|
|
||||||
|
SynchronizeMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModifiedSequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forcibly remove a texture from this pool's items.
|
/// Forcibly remove a texture from this pool's items.
|
||||||
/// If deferred, the dereference will be queued to occur on the render thread.
|
/// If deferred, the dereference will be queued to occur on the render thread.
|
||||||
@ -175,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="descriptor">The texture descriptor</param>
|
/// <param name="descriptor">The texture descriptor</param>
|
||||||
/// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
|
/// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
|
||||||
/// <returns>The texture information</returns>
|
/// <returns>The texture information</returns>
|
||||||
private TextureInfo GetInfo(TextureDescriptor descriptor, out int layerSize)
|
private TextureInfo GetInfo(in TextureDescriptor descriptor, out int layerSize)
|
||||||
{
|
{
|
||||||
int depthOrLayers = descriptor.UnpackDepth();
|
int depthOrLayers = descriptor.UnpackDepth();
|
||||||
int levels = descriptor.UnpackLevels();
|
int levels = descriptor.UnpackLevels();
|
||||||
|
@ -378,6 +378,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return _gpUniformBuffers[stage].Buffers[index].Address;
|
return _gpUniformBuffers[stage].Buffers[index].Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the bounds of the uniform buffer currently bound at the given index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isCompute">Indicates whenever the uniform is requested by the 3D or compute engine</param>
|
||||||
|
/// <param name="stage">Index of the shader stage, if the uniform is for the 3D engine</param>
|
||||||
|
/// <param name="index">Index of the uniform buffer binding</param>
|
||||||
|
/// <returns>The uniform buffer bounds, or an undefined value if the buffer is not currently bound</returns>
|
||||||
|
public ref BufferBounds GetUniformBufferBounds(bool isCompute, int stage, int index)
|
||||||
|
{
|
||||||
|
if (isCompute)
|
||||||
|
{
|
||||||
|
return ref _cpUniformBuffers.Buffers[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ref _gpUniformBuffers[stage].Buffers[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensures that the compute engine bindings are visible to the host GPU.
|
/// Ensures that the compute engine bindings are visible to the host GPU.
|
||||||
/// Note: this actually performs the binding using the host graphics API.
|
/// Note: this actually performs the binding using the host graphics API.
|
||||||
|
@ -35,6 +35,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
HostProgram = hostProgram;
|
HostProgram = hostProgram;
|
||||||
SpecializationState = specializationState;
|
SpecializationState = specializationState;
|
||||||
Shaders = shaders;
|
Shaders = shaders;
|
||||||
|
|
||||||
|
SpecializationState.Prepare(shaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -418,7 +418,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
|
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
|
||||||
{
|
{
|
||||||
return cpShader.SpecializationState.MatchesCompute(channel, poolState);
|
return cpShader.SpecializationState.MatchesCompute(channel, poolState, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState);
|
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
foreach (var entry in _entries)
|
foreach (var entry in _entries)
|
||||||
{
|
{
|
||||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState))
|
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true))
|
||||||
{
|
{
|
||||||
program = entry;
|
program = entry;
|
||||||
return true;
|
return true;
|
||||||
@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
foreach (var entry in _entries)
|
foreach (var entry in _entries)
|
||||||
{
|
{
|
||||||
if (entry.SpecializationState.MatchesCompute(channel, poolState))
|
if (entry.SpecializationState.MatchesCompute(channel, poolState, true))
|
||||||
{
|
{
|
||||||
program = entry;
|
program = entry;
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
|
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Shader
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
@ -158,6 +163,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
|
private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
|
||||||
|
private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures;
|
||||||
|
private Box<TextureSpecializationState>[][] _textureByBinding;
|
||||||
|
private Box<TextureSpecializationState>[][] _imageByBinding;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the shader specialization state.
|
/// Creates a new instance of the shader specialization state.
|
||||||
@ -194,6 +202,48 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare the shader specialization state for quick binding lookups.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stages">The shader stages</param>
|
||||||
|
public void Prepare(CachedShaderStage[] stages)
|
||||||
|
{
|
||||||
|
_allTextures = _textureSpecialization.ToArray();
|
||||||
|
|
||||||
|
_textureByBinding = new Box<TextureSpecializationState>[stages.Length][];
|
||||||
|
_imageByBinding = new Box<TextureSpecializationState>[stages.Length][];
|
||||||
|
|
||||||
|
for (int i = 0; i < stages.Length; i++)
|
||||||
|
{
|
||||||
|
CachedShaderStage stage = stages[i];
|
||||||
|
if (stage?.Info != null)
|
||||||
|
{
|
||||||
|
var textures = stage.Info.Textures;
|
||||||
|
var images = stage.Info.Images;
|
||||||
|
|
||||||
|
var texBindings = new Box<TextureSpecializationState>[textures.Count];
|
||||||
|
var imageBindings = new Box<TextureSpecializationState>[images.Count];
|
||||||
|
|
||||||
|
int stageIndex = Math.Max(i - 1, 0); // Don't count VertexA for looking up spec state. No-Op for compute.
|
||||||
|
|
||||||
|
for (int j = 0; j < textures.Count; j++)
|
||||||
|
{
|
||||||
|
var texture = textures[j];
|
||||||
|
texBindings[j] = GetTextureSpecState(stageIndex, texture.HandleIndex, texture.CbufSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < images.Count; j++)
|
||||||
|
{
|
||||||
|
var image = images[j];
|
||||||
|
imageBindings[j] = GetTextureSpecState(stageIndex, image.HandleIndex, image.CbufSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textureByBinding[i] = texBindings;
|
||||||
|
_imageByBinding[i] = imageBindings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the shader accesses the early Z force state.
|
/// Indicates that the shader accesses the early Z force state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -396,15 +446,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
/// <param name="graphicsState">Graphics state</param>
|
/// <param name="graphicsState">Graphics state</param>
|
||||||
|
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||||
/// <returns>True if the state matches, false otherwise</returns>
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState)
|
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState, bool checkTextures)
|
||||||
{
|
{
|
||||||
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
|
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Matches(channel, poolState, isCompute: false);
|
return Matches(channel, poolState, checkTextures, isCompute: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -412,10 +463,64 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||||
/// <returns>True if the state matches, false otherwise</returns>
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState)
|
public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures)
|
||||||
{
|
{
|
||||||
return Matches(channel, poolState, isCompute: true);
|
return Matches(channel, poolState, checkTextures, isCompute: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetch the constant buffers used for a texture to cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel">GPU channel</param>
|
||||||
|
/// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
|
||||||
|
/// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param>
|
||||||
|
/// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param>
|
||||||
|
/// <param name="cachedTextureBuffer">The currently cached texture buffer data</param>
|
||||||
|
/// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param>
|
||||||
|
/// <param name="cachedStageIndex">The currently cached stage</param>
|
||||||
|
/// <param name="textureBufferIndex">The new texture buffer index</param>
|
||||||
|
/// <param name="samplerBufferIndex">The new sampler buffer index</param>
|
||||||
|
/// <param name="stageIndex">Stage index of the constant buffer</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void UpdateCachedBuffer(
|
||||||
|
GpuChannel channel,
|
||||||
|
bool isCompute,
|
||||||
|
ref int cachedTextureBufferIndex,
|
||||||
|
ref int cachedSamplerBufferIndex,
|
||||||
|
ref ReadOnlySpan<int> cachedTextureBuffer,
|
||||||
|
ref ReadOnlySpan<int> cachedSamplerBuffer,
|
||||||
|
ref int cachedStageIndex,
|
||||||
|
int textureBufferIndex,
|
||||||
|
int samplerBufferIndex,
|
||||||
|
int stageIndex)
|
||||||
|
{
|
||||||
|
bool stageChange = stageIndex != cachedStageIndex;
|
||||||
|
|
||||||
|
if (stageChange || textureBufferIndex != cachedTextureBufferIndex)
|
||||||
|
{
|
||||||
|
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
|
||||||
|
cachedTextureBufferIndex = textureBufferIndex;
|
||||||
|
|
||||||
|
if (samplerBufferIndex == textureBufferIndex)
|
||||||
|
{
|
||||||
|
cachedSamplerBuffer = cachedTextureBuffer;
|
||||||
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stageChange || samplerBufferIndex != cachedSamplerBufferIndex)
|
||||||
|
{
|
||||||
|
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
|
||||||
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedStageIndex = stageIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -423,9 +528,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||||
/// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
|
/// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
|
||||||
/// <returns>True if the state matches, false otherwise</returns>
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
private bool Matches(GpuChannel channel, GpuChannelPoolState poolState, bool isCompute)
|
private bool Matches(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures, bool isCompute)
|
||||||
{
|
{
|
||||||
int constantBufferUsePerStageMask = _constantBufferUsePerStage;
|
int constantBufferUsePerStageMask = _constantBufferUsePerStage;
|
||||||
|
|
||||||
@ -445,55 +551,60 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
constantBufferUsePerStageMask &= ~(1 << index);
|
constantBufferUsePerStageMask &= ~(1 << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var kv in _textureSpecialization)
|
if (checkTextures)
|
||||||
{
|
{
|
||||||
TextureKey textureKey = kv.Key;
|
TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
|
||||||
|
|
||||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(textureKey.CbufSlot, poolState.TextureBufferIndex);
|
int cachedTextureBufferIndex = -1;
|
||||||
|
int cachedSamplerBufferIndex = -1;
|
||||||
|
int cachedStageIndex = -1;
|
||||||
|
ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty;
|
||||||
|
ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty;
|
||||||
|
|
||||||
ulong textureCbAddress;
|
foreach (var kv in _allTextures)
|
||||||
ulong samplerCbAddress;
|
|
||||||
|
|
||||||
if (isCompute)
|
|
||||||
{
|
{
|
||||||
textureCbAddress = channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex);
|
TextureKey textureKey = kv.Key;
|
||||||
samplerCbAddress = channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
textureCbAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(textureKey.StageIndex, textureBufferIndex);
|
|
||||||
samplerCbAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(textureKey.StageIndex, samplerBufferIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!channel.MemoryManager.Physical.IsMapped(textureCbAddress) || !channel.MemoryManager.Physical.IsMapped(samplerCbAddress))
|
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(textureKey.CbufSlot, poolState.TextureBufferIndex);
|
||||||
{
|
|
||||||
continue;
|
UpdateCachedBuffer(channel,
|
||||||
|
isCompute,
|
||||||
|
ref cachedTextureBufferIndex,
|
||||||
|
ref cachedSamplerBufferIndex,
|
||||||
|
ref cachedTextureBuffer,
|
||||||
|
ref cachedSamplerBuffer,
|
||||||
|
ref cachedStageIndex,
|
||||||
|
textureBufferIndex,
|
||||||
|
samplerBufferIndex,
|
||||||
|
textureKey.StageIndex);
|
||||||
|
|
||||||
|
int packedId = TextureHandle.ReadPackedId(textureKey.Handle, cachedTextureBuffer, cachedSamplerBuffer);
|
||||||
|
|
||||||
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
|
|
||||||
|
ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId);
|
||||||
|
|
||||||
|
if (!MatchesTexture(kv.Value, descriptor))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image.TextureDescriptor descriptor;
|
return true;
|
||||||
|
}
|
||||||
if (isCompute)
|
|
||||||
{
|
|
||||||
descriptor = channel.TextureManager.GetComputeTextureDescriptor(
|
|
||||||
poolState.TexturePoolGpuVa,
|
|
||||||
poolState.TextureBufferIndex,
|
|
||||||
poolState.TexturePoolMaximumId,
|
|
||||||
textureKey.Handle,
|
|
||||||
textureKey.CbufSlot);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
descriptor = channel.TextureManager.GetGraphicsTextureDescriptor(
|
|
||||||
poolState.TexturePoolGpuVa,
|
|
||||||
poolState.TextureBufferIndex,
|
|
||||||
poolState.TexturePoolMaximumId,
|
|
||||||
textureKey.StageIndex,
|
|
||||||
textureKey.Handle,
|
|
||||||
textureKey.CbufSlot);
|
|
||||||
}
|
|
||||||
|
|
||||||
Box<TextureSpecializationState> specializationState = kv.Value;
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the recorded texture state matches the given texture descriptor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="specializationState">Texture specialization state</param>
|
||||||
|
/// <param name="descriptor">Texture descriptor</param>
|
||||||
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private bool MatchesTexture(Box<TextureSpecializationState> specializationState, in Image.TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (specializationState != null)
|
||||||
|
{
|
||||||
if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) &&
|
if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) &&
|
||||||
specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized())
|
specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized())
|
||||||
{
|
{
|
||||||
@ -504,6 +615,34 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the recorded texture state for a given texture binding matches a texture descriptor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stage">The shader stage</param>
|
||||||
|
/// <param name="index">The texture index</param>
|
||||||
|
/// <param name="descriptor">Texture descriptor</param>
|
||||||
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
|
public bool MatchesTexture(ShaderStage stage, int index, in Image.TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
Box<TextureSpecializationState> specializationState = _textureByBinding[(int)stage][index];
|
||||||
|
|
||||||
|
return MatchesTexture(specializationState, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the recorded texture state for a given image binding matches a texture descriptor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stage">The shader stage</param>
|
||||||
|
/// <param name="index">The texture index</param>
|
||||||
|
/// <param name="descriptor">Texture descriptor</param>
|
||||||
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
|
public bool MatchesImage(ShaderStage stage, int index, in Image.TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
Box<TextureSpecializationState> specializationState = _imageByBinding[(int)stage][index];
|
||||||
|
|
||||||
|
return MatchesTexture(specializationState, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads shader specialization state that has been serialized.
|
/// Reads shader specialization state that has been serialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -9,10 +9,13 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
class Framebuffer : IDisposable
|
class Framebuffer : IDisposable
|
||||||
{
|
{
|
||||||
public int Handle { get; private set; }
|
public int Handle { get; private set; }
|
||||||
|
private int _clearFbHandle;
|
||||||
|
private bool _clearFbInitialized;
|
||||||
|
|
||||||
private FramebufferAttachment _lastDsAttachment;
|
private FramebufferAttachment _lastDsAttachment;
|
||||||
|
|
||||||
private readonly TextureView[] _colors;
|
private readonly TextureView[] _colors;
|
||||||
|
private TextureView _depthStencil;
|
||||||
|
|
||||||
private int _colorsCount;
|
private int _colorsCount;
|
||||||
private bool _dualSourceBlend;
|
private bool _dualSourceBlend;
|
||||||
@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public Framebuffer()
|
public Framebuffer()
|
||||||
{
|
{
|
||||||
Handle = GL.GenFramebuffer();
|
Handle = GL.GenFramebuffer();
|
||||||
|
_clearFbHandle = GL.GenFramebuffer();
|
||||||
|
|
||||||
_colors = new TextureView[8];
|
_colors = new TextureView[8];
|
||||||
}
|
}
|
||||||
@ -55,20 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
if (depthStencil != null)
|
if (depthStencil != null)
|
||||||
{
|
{
|
||||||
FramebufferAttachment attachment;
|
FramebufferAttachment attachment = GetAttachment(depthStencil.Format);
|
||||||
|
|
||||||
if (IsPackedDepthStencilFormat(depthStencil.Format))
|
|
||||||
{
|
|
||||||
attachment = FramebufferAttachment.DepthStencilAttachment;
|
|
||||||
}
|
|
||||||
else if (IsDepthOnlyFormat(depthStencil.Format))
|
|
||||||
{
|
|
||||||
attachment = FramebufferAttachment.DepthAttachment;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attachment = FramebufferAttachment.StencilAttachment;
|
|
||||||
}
|
|
||||||
|
|
||||||
GL.FramebufferTexture(
|
GL.FramebufferTexture(
|
||||||
FramebufferTarget.Framebuffer,
|
FramebufferTarget.Framebuffer,
|
||||||
@ -82,6 +73,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
_lastDsAttachment = 0;
|
_lastDsAttachment = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_depthStencil = depthStencil;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDualSourceBlend(bool enable)
|
public void SetDualSourceBlend(bool enable)
|
||||||
@ -124,6 +117,22 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.DrawBuffers(colorsCount, drawBuffers);
|
GL.DrawBuffers(colorsCount, drawBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FramebufferAttachment GetAttachment(Format format)
|
||||||
|
{
|
||||||
|
if (IsPackedDepthStencilFormat(format))
|
||||||
|
{
|
||||||
|
return FramebufferAttachment.DepthStencilAttachment;
|
||||||
|
}
|
||||||
|
else if (IsDepthOnlyFormat(format))
|
||||||
|
{
|
||||||
|
return FramebufferAttachment.DepthAttachment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FramebufferAttachment.StencilAttachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsPackedDepthStencilFormat(Format format)
|
private static bool IsPackedDepthStencilFormat(Format format)
|
||||||
{
|
{
|
||||||
return format == Format.D24UnormS8Uint ||
|
return format == Format.D24UnormS8Uint ||
|
||||||
@ -136,6 +145,78 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
return format == Format.D16Unorm || format == Format.D32Float;
|
return format == Format.D16Unorm || format == Format.D32Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AttachColorLayerForClear(int index, int layer)
|
||||||
|
{
|
||||||
|
TextureView color = _colors[index];
|
||||||
|
|
||||||
|
if (!IsLayered(color))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindClearFb();
|
||||||
|
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, color.Handle, 0, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DetachColorLayerForClear(int index)
|
||||||
|
{
|
||||||
|
TextureView color = _colors[index];
|
||||||
|
|
||||||
|
if (!IsLayered(color))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, 0, 0);
|
||||||
|
Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AttachDepthStencilLayerForClear(int layer)
|
||||||
|
{
|
||||||
|
TextureView depthStencil = _depthStencil;
|
||||||
|
|
||||||
|
if (!IsLayered(depthStencil))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindClearFb();
|
||||||
|
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), depthStencil.Handle, 0, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DetachDepthStencilLayerForClear()
|
||||||
|
{
|
||||||
|
TextureView depthStencil = _depthStencil;
|
||||||
|
|
||||||
|
if (!IsLayered(depthStencil))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.FramebufferTexture(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), 0, 0);
|
||||||
|
Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BindClearFb()
|
||||||
|
{
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, _clearFbHandle);
|
||||||
|
|
||||||
|
if (!_clearFbInitialized)
|
||||||
|
{
|
||||||
|
SetDrawBuffersImpl(Constants.MaxRenderTargets);
|
||||||
|
_clearFbInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsLayered(TextureView view)
|
||||||
|
{
|
||||||
|
return view != null &&
|
||||||
|
view.Target != Target.Texture1D &&
|
||||||
|
view.Target != Target.Texture2D &&
|
||||||
|
view.Target != Target.Texture2DMultisample &&
|
||||||
|
view.Target != Target.TextureBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (Handle != 0)
|
if (Handle != 0)
|
||||||
@ -144,6 +225,13 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
Handle = 0;
|
Handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_clearFbHandle != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteFramebuffer(_clearFbHandle);
|
||||||
|
|
||||||
|
_clearFbHandle = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
98
Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
Normal file
98
Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
{
|
||||||
|
class IntermmediatePool : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Renderer _renderer;
|
||||||
|
private readonly List<TextureView> _entries;
|
||||||
|
|
||||||
|
public IntermmediatePool(Renderer renderer)
|
||||||
|
{
|
||||||
|
_renderer = renderer;
|
||||||
|
_entries = new List<TextureView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureView GetOrCreateWithAtLeast(
|
||||||
|
Target target,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int bytesPerPixel,
|
||||||
|
Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depth,
|
||||||
|
int levels)
|
||||||
|
{
|
||||||
|
TextureView entry;
|
||||||
|
|
||||||
|
for (int i = 0; i < _entries.Count; i++)
|
||||||
|
{
|
||||||
|
entry = _entries[i];
|
||||||
|
|
||||||
|
if (entry.Target == target && entry.Format == format)
|
||||||
|
{
|
||||||
|
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
|
||||||
|
{
|
||||||
|
width = Math.Max(width, entry.Width);
|
||||||
|
height = Math.Max(height, entry.Height);
|
||||||
|
depth = Math.Max(depth, entry.Info.Depth);
|
||||||
|
levels = Math.Max(levels, entry.Info.Levels);
|
||||||
|
|
||||||
|
entry.Dispose();
|
||||||
|
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
|
||||||
|
_entries[i] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
|
||||||
|
_entries.Add(entry);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextureView CreateNew(
|
||||||
|
Target target,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int bytesPerPixel,
|
||||||
|
Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depth,
|
||||||
|
int levels)
|
||||||
|
{
|
||||||
|
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
levels,
|
||||||
|
1,
|
||||||
|
blockWidth,
|
||||||
|
blockHeight,
|
||||||
|
bytesPerPixel,
|
||||||
|
format,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
target,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha), 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (TextureView entry in _entries)
|
||||||
|
{
|
||||||
|
entry.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_entries.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
{
|
{
|
||||||
private readonly Renderer _renderer;
|
private readonly Renderer _renderer;
|
||||||
|
|
||||||
|
public IntermmediatePool IntermmediatePool { get; }
|
||||||
|
|
||||||
private int _srcFramebuffer;
|
private int _srcFramebuffer;
|
||||||
private int _dstFramebuffer;
|
private int _dstFramebuffer;
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
public TextureCopy(Renderer renderer)
|
public TextureCopy(Renderer renderer)
|
||||||
{
|
{
|
||||||
_renderer = renderer;
|
_renderer = renderer;
|
||||||
|
IntermmediatePool = new IntermmediatePool(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Copy(
|
public void Copy(
|
||||||
@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
TextureView dst,
|
TextureView dst,
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion,
|
Extents2D dstRegion,
|
||||||
bool linearFilter)
|
bool linearFilter,
|
||||||
|
int srcLayer = 0,
|
||||||
|
int dstLayer = 0,
|
||||||
|
int srcLevel = 0,
|
||||||
|
int dstLevel = 0)
|
||||||
|
{
|
||||||
|
int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel);
|
||||||
|
int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer);
|
||||||
|
|
||||||
|
Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Copy(
|
||||||
|
TextureView src,
|
||||||
|
TextureView dst,
|
||||||
|
Extents2D srcRegion,
|
||||||
|
Extents2D dstRegion,
|
||||||
|
bool linearFilter,
|
||||||
|
int srcLayer,
|
||||||
|
int dstLayer,
|
||||||
|
int srcLevel,
|
||||||
|
int dstLevel,
|
||||||
|
int layers,
|
||||||
|
int levels)
|
||||||
{
|
{
|
||||||
TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
|
TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
|
||||||
|
|
||||||
@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
|
||||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
|
||||||
|
|
||||||
int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
|
if (srcLevel != 0)
|
||||||
int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers());
|
{
|
||||||
|
srcRegion = srcRegion.Reduce(srcLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dstLevel != 0)
|
||||||
|
{
|
||||||
|
dstRegion = dstRegion.Reduce(dstLevel);
|
||||||
|
}
|
||||||
|
|
||||||
for (int level = 0; level < levels; level++)
|
for (int level = 0; level < levels; level++)
|
||||||
{
|
{
|
||||||
for (int layer = 0; layer < layers; layer++)
|
for (int layer = 0; layer < layers; layer++)
|
||||||
{
|
{
|
||||||
if (layers > 1)
|
if ((srcLayer | dstLayer) != 0 || layers > 1)
|
||||||
{
|
{
|
||||||
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer);
|
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer);
|
||||||
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer);
|
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level);
|
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level);
|
||||||
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level);
|
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearBufferMask mask = GetMask(src.Format);
|
ClearBufferMask mask = GetMask(src.Format);
|
||||||
@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
_copyPboHandle = 0;
|
_copyPboHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IntermmediatePool.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
{
|
{
|
||||||
TextureView destinationView = (TextureView)destination;
|
TextureView destinationView = (TextureView)destination;
|
||||||
|
|
||||||
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
|
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
|
||||||
|
{
|
||||||
|
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
||||||
|
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
||||||
|
|
||||||
|
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
|
||||||
|
GetIntermmediateTarget(Target),
|
||||||
|
Info.BlockWidth,
|
||||||
|
Info.BlockHeight,
|
||||||
|
Info.BytesPerPixel,
|
||||||
|
Format,
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
Info.Depth,
|
||||||
|
Info.Levels);
|
||||||
|
|
||||||
|
GL.Disable(EnableCap.FramebufferSrgb);
|
||||||
|
|
||||||
|
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
|
||||||
|
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.FramebufferSrgb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
|
public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
|
||||||
{
|
{
|
||||||
TextureView destinationView = (TextureView)destination;
|
TextureView destinationView = (TextureView)destination;
|
||||||
|
|
||||||
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
|
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
|
||||||
|
{
|
||||||
|
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
|
||||||
|
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
|
||||||
|
|
||||||
|
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
|
||||||
|
GetIntermmediateTarget(Target),
|
||||||
|
Info.BlockWidth,
|
||||||
|
Info.BlockHeight,
|
||||||
|
Info.BytesPerPixel,
|
||||||
|
Format,
|
||||||
|
Math.Max(1, Width >> srcLevel),
|
||||||
|
Math.Max(1, Height >> srcLevel),
|
||||||
|
1,
|
||||||
|
1);
|
||||||
|
|
||||||
|
GL.Disable(EnableCap.FramebufferSrgb);
|
||||||
|
|
||||||
|
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
|
||||||
|
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.FramebufferSrgb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Target GetIntermmediateTarget(Target srcTarget)
|
||||||
|
{
|
||||||
|
return srcTarget switch
|
||||||
|
{
|
||||||
|
Target.Texture2D => Target.Texture2DMultisample,
|
||||||
|
Target.Texture2DArray => Target.Texture2DMultisampleArray,
|
||||||
|
Target.Texture2DMultisampleArray => Target.Texture2DArray,
|
||||||
|
_ => Target.Texture2D
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
|
||||||
|
@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
Buffer.Clear(destination, offset, size, value);
|
Buffer.Clear(destination, offset, size, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
|
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
|
||||||
{
|
{
|
||||||
GL.ColorMask(
|
GL.ColorMask(
|
||||||
index,
|
index,
|
||||||
@ -119,14 +119,18 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
(componentMask & 4) != 0,
|
(componentMask & 4) != 0,
|
||||||
(componentMask & 8) != 0);
|
(componentMask & 8) != 0);
|
||||||
|
|
||||||
|
_framebuffer.AttachColorLayerForClear(index, layer);
|
||||||
|
|
||||||
float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
|
float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
|
||||||
|
|
||||||
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
|
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
|
||||||
|
|
||||||
|
_framebuffer.DetachColorLayerForClear(index);
|
||||||
|
|
||||||
RestoreComponentMask(index);
|
RestoreComponentMask(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||||
{
|
{
|
||||||
bool stencilMaskChanged =
|
bool stencilMaskChanged =
|
||||||
stencilMask != 0 &&
|
stencilMask != 0 &&
|
||||||
@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.DepthMask(depthMask);
|
GL.DepthMask(depthMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_framebuffer.AttachDepthStencilLayerForClear(layer);
|
||||||
|
|
||||||
if (depthMask && stencilMask != 0)
|
if (depthMask && stencilMask != 0)
|
||||||
{
|
{
|
||||||
GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
|
GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
|
||||||
@ -157,6 +163,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
|
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_framebuffer.DetachDepthStencilLayerForClear();
|
||||||
|
|
||||||
if (stencilMaskChanged)
|
if (stencilMaskChanged)
|
||||||
{
|
{
|
||||||
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
|
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
|
||||||
@ -597,6 +605,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.EndTransformFeedback();
|
GL.EndTransformFeedback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GL.ClipControl(ClipOrigin.UpperLeft, ClipDepthMode.NegativeOneToOne);
|
||||||
|
|
||||||
_drawTexture.Draw(
|
_drawTexture.Draw(
|
||||||
view,
|
view,
|
||||||
samp,
|
samp,
|
||||||
@ -627,6 +637,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
GL.BeginTransformFeedback(_tfTopology);
|
GL.BeginTransformFeedback(_tfTopology);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RestoreClipControl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader
|
namespace Ryujinx.Graphics.Shader
|
||||||
@ -50,5 +51,63 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
{
|
{
|
||||||
return (handle & 0x3fff, (handle >> 14) & 0x3fff, (TextureHandleType)((uint)handle >> 28));
|
return (handle & 0x3fff, (handle >> 14) & 0x3fff, (TextureHandleType)((uint)handle >> 28));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks the texture ID from the real texture handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packedId">The real texture handle</param>
|
||||||
|
/// <returns>The texture ID</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int UnpackTextureId(int packedId)
|
||||||
|
{
|
||||||
|
return (packedId >> 0) & 0xfffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks the sampler ID from the real texture handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packedId">The real texture handle</param>
|
||||||
|
/// <returns>The sampler ID</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int UnpackSamplerId(int packedId)
|
||||||
|
{
|
||||||
|
return (packedId >> 20) & 0xfff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a packed texture and sampler ID (basically, the real texture handle)
|
||||||
|
/// from a given texture/sampler constant buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wordOffset">A word offset of the handle on the buffer (the "fake" shader handle)</param>
|
||||||
|
/// <param name="cachedTextureBuffer">The constant buffer to fetch texture IDs from</param>
|
||||||
|
/// <param name="cachedSamplerBuffer">The constant buffer to fetch sampler IDs from</param>
|
||||||
|
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int ReadPackedId(int wordOffset, ReadOnlySpan<int> cachedTextureBuffer, ReadOnlySpan<int> cachedSamplerBuffer)
|
||||||
|
{
|
||||||
|
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = UnpackOffsets(wordOffset);
|
||||||
|
|
||||||
|
int handle = cachedTextureBuffer[textureWordOffset];
|
||||||
|
|
||||||
|
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
||||||
|
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
|
||||||
|
// bindless textures on the shader), we extend it with another value on the higher 16 bits with
|
||||||
|
// another offset for the sampler.
|
||||||
|
// The shader translator has code to detect separate texture and sampler uses with a bindless texture,
|
||||||
|
// turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
|
||||||
|
if (handleType != TextureHandleType.CombinedSampler)
|
||||||
|
{
|
||||||
|
int samplerHandle = cachedSamplerBuffer[samplerWordOffset];
|
||||||
|
|
||||||
|
if (handleType == TextureHandleType.SeparateSamplerId)
|
||||||
|
{
|
||||||
|
samplerHandle <<= 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle |= samplerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,22 @@ namespace Ryujinx.Graphics.Vic
|
|||||||
{
|
{
|
||||||
static class Blender
|
static class Blender
|
||||||
{
|
{
|
||||||
public static void BlendOne(Surface dst, Surface src, ref SlotStruct slot)
|
public static void BlendOne(Surface dst, Surface src, ref SlotStruct slot, Rectangle targetRect)
|
||||||
{
|
{
|
||||||
if (Sse41.IsSupported && (dst.Width & 3) == 0)
|
int x1 = targetRect.X;
|
||||||
|
int y1 = targetRect.Y;
|
||||||
|
int x2 = Math.Min(src.Width, x1 + targetRect.Width);
|
||||||
|
int y2 = Math.Min(src.Height, y1 + targetRect.Height);
|
||||||
|
|
||||||
|
if (Sse41.IsSupported && ((x1 | x2) & 3) == 0)
|
||||||
{
|
{
|
||||||
BlendOneSse41(dst, src, ref slot);
|
BlendOneSse41(dst, src, ref slot, x1, y1, x2, y2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < dst.Height; y++)
|
for (int y = y1; y < y2; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < dst.Width; x++)
|
for (int x = x1; x < x2; x++)
|
||||||
{
|
{
|
||||||
int inR = src.GetR(x, y);
|
int inR = src.GetR(x, y);
|
||||||
int inG = src.GetG(x, y);
|
int inG = src.GetG(x, y);
|
||||||
@ -40,9 +45,9 @@ namespace Ryujinx.Graphics.Vic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe static void BlendOneSse41(Surface dst, Surface src, ref SlotStruct slot)
|
private unsafe static void BlendOneSse41(Surface dst, Surface src, ref SlotStruct slot, int x1, int y1, int x2, int y2)
|
||||||
{
|
{
|
||||||
Debug.Assert((dst.Width & 3) == 0);
|
Debug.Assert(((x1 | x2) & 3) == 0);
|
||||||
|
|
||||||
ref MatrixStruct mtx = ref slot.ColorMatrixStruct;
|
ref MatrixStruct mtx = ref slot.ColorMatrixStruct;
|
||||||
|
|
||||||
@ -62,9 +67,9 @@ namespace Ryujinx.Graphics.Vic
|
|||||||
Pixel* ip = srcPtr;
|
Pixel* ip = srcPtr;
|
||||||
Pixel* op = dstPtr;
|
Pixel* op = dstPtr;
|
||||||
|
|
||||||
for (int y = 0; y < dst.Height; y++, ip += src.Width, op += dst.Width)
|
for (int y = y1; y < y2; y++, ip += src.Width, op += dst.Width)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < dst.Width; x += 4)
|
for (int x = x1; x < x2; x += 4)
|
||||||
{
|
{
|
||||||
Vector128<int> pixel1 = Sse41.ConvertToVector128Int32((ushort*)(ip + (uint)x));
|
Vector128<int> pixel1 = Sse41.ConvertToVector128Int32((ushort*)(ip + (uint)x));
|
||||||
Vector128<int> pixel2 = Sse41.ConvertToVector128Int32((ushort*)(ip + (uint)x + 1));
|
Vector128<int> pixel2 = Sse41.ConvertToVector128Int32((ushort*)(ip + (uint)x + 1));
|
||||||
|
18
Ryujinx.Graphics.Vic/Rectangle.cs
Normal file
18
Ryujinx.Graphics.Vic/Rectangle.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Ryujinx.Graphics.Vic
|
||||||
|
{
|
||||||
|
struct Rectangle
|
||||||
|
{
|
||||||
|
public readonly int X;
|
||||||
|
public readonly int Y;
|
||||||
|
public readonly int Width;
|
||||||
|
public readonly int Height;
|
||||||
|
|
||||||
|
public Rectangle(int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Vic.Image;
|
using Ryujinx.Graphics.Vic.Image;
|
||||||
using Ryujinx.Graphics.Vic.Types;
|
using Ryujinx.Graphics.Vic.Types;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vic
|
namespace Ryujinx.Graphics.Vic
|
||||||
@ -47,7 +48,19 @@ namespace Ryujinx.Graphics.Vic
|
|||||||
|
|
||||||
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets);
|
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets);
|
||||||
|
|
||||||
Blender.BlendOne(output, src, ref slot);
|
int x1 = config.OutputConfig.TargetRectLeft;
|
||||||
|
int y1 = config.OutputConfig.TargetRectTop;
|
||||||
|
int x2 = config.OutputConfig.TargetRectRight + 1;
|
||||||
|
int y2 = config.OutputConfig.TargetRectBottom + 1;
|
||||||
|
|
||||||
|
int targetX = Math.Min(x1, x2);
|
||||||
|
int targetY = Math.Min(y1, y2);
|
||||||
|
int targetW = Math.Min(output.Width - targetX, Math.Abs(x2 - x1));
|
||||||
|
int targetH = Math.Min(output.Height - targetY, Math.Abs(y2 - y1));
|
||||||
|
|
||||||
|
Rectangle targetRect = new Rectangle(targetX, targetY, targetW, targetH);
|
||||||
|
|
||||||
|
Blender.BlendOne(output, src, ref slot, targetRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceWriter.Write(_rm, output, ref config.OutputSurfaceConfig, ref _state.State.SetOutputSurface);
|
SurfaceWriter.Write(_rm, output, ref config.OutputSurfaceConfig, ref _state.State.SetOutputSurface);
|
||||||
|
@ -280,13 +280,6 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainNca == null)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "Unable to load NSP: Could not find Main NCA");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainNca != null)
|
if (mainNca != null)
|
||||||
{
|
{
|
||||||
_device.Configuration.ContentManager.ClearAocData();
|
_device.Configuration.ContentManager.ClearAocData();
|
||||||
@ -298,7 +291,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
||||||
LoadExeFs(nsp);
|
LoadExeFs(nsp, null, isHomebrew: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNca(string ncaFile)
|
public void LoadNca(string ncaFile)
|
||||||
@ -593,7 +586,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null)
|
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false)
|
||||||
{
|
{
|
||||||
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
||||||
{
|
{
|
||||||
@ -661,7 +654,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
|
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
|
||||||
|
|
||||||
// We allow it for nx-hbloader because it can be used to launch homebrew.
|
// We allow it for nx-hbloader because it can be used to launch homebrew.
|
||||||
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL;
|
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
|
||||||
|
|
||||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||||
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);
|
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
@ -11,12 +10,12 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
private readonly ulong _pid;
|
private readonly ulong _pid;
|
||||||
private readonly GpuContext _gpuContext;
|
private readonly GpuContext _gpuContext;
|
||||||
private readonly CpuContext _cpuContext;
|
private readonly ICpuContext _cpuContext;
|
||||||
private T _memoryManager;
|
private T _memoryManager;
|
||||||
|
|
||||||
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
||||||
|
|
||||||
public ArmProcessContext(ulong pid, GpuContext gpuContext, T memoryManager, bool for64Bit)
|
public ArmProcessContext(ulong pid, ICpuEngine cpuEngine, GpuContext gpuContext, T memoryManager, bool for64Bit)
|
||||||
{
|
{
|
||||||
if (memoryManager is IRefCounted rc)
|
if (memoryManager is IRefCounted rc)
|
||||||
{
|
{
|
||||||
@ -27,11 +26,16 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
_pid = pid;
|
_pid = pid;
|
||||||
_gpuContext = gpuContext;
|
_gpuContext = gpuContext;
|
||||||
_cpuContext = new CpuContext(memoryManager, for64Bit);
|
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit);
|
||||||
_memoryManager = memoryManager;
|
_memoryManager = memoryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ExecutionContext context, ulong codeAddress)
|
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||||
|
{
|
||||||
|
return _cpuContext.CreateExecutionContext(exceptionCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(IExecutionContext context, ulong codeAddress)
|
||||||
{
|
{
|
||||||
_cpuContext.Execute(context, codeAddress);
|
_cpuContext.Execute(context, codeAddress);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.Cpu.Jit;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
@ -10,10 +11,12 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
class ArmProcessContextFactory : IProcessContextFactory
|
class ArmProcessContextFactory : IProcessContextFactory
|
||||||
{
|
{
|
||||||
|
private readonly ICpuEngine _cpuEngine;
|
||||||
private readonly GpuContext _gpu;
|
private readonly GpuContext _gpu;
|
||||||
|
|
||||||
public ArmProcessContextFactory(GpuContext gpu)
|
public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu)
|
||||||
{
|
{
|
||||||
|
_cpuEngine = cpuEngine;
|
||||||
_gpu = gpu;
|
_gpu = gpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,12 +32,14 @@ namespace Ryujinx.HLE.HOS
|
|||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case MemoryManagerMode.SoftwarePageTable:
|
case MemoryManagerMode.SoftwarePageTable:
|
||||||
return new ArmProcessContext<MemoryManager>(pid, _gpu, new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler), for64Bit);
|
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||||
|
return new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
|
||||||
|
|
||||||
case MemoryManagerMode.HostMapped:
|
case MemoryManagerMode.HostMapped:
|
||||||
case MemoryManagerMode.HostMappedUnsafe:
|
case MemoryManagerMode.HostMappedUnsafe:
|
||||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
||||||
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _gpu, new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
|
var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler);
|
||||||
|
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
@ -10,6 +10,8 @@ using Ryujinx.Audio.Integration;
|
|||||||
using Ryujinx.Audio.Output;
|
using Ryujinx.Audio.Output;
|
||||||
using Ryujinx.Audio.Renderer.Device;
|
using Ryujinx.Audio.Renderer.Device;
|
||||||
using Ryujinx.Audio.Renderer.Server;
|
using Ryujinx.Audio.Renderer.Server;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.Cpu.Jit;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
@ -57,6 +59,9 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
internal Switch Device { get; private set; }
|
internal Switch Device { get; private set; }
|
||||||
|
|
||||||
|
internal ITickSource TickSource { get; }
|
||||||
|
internal ICpuEngine CpuEngine { get; }
|
||||||
|
|
||||||
internal SurfaceFlinger SurfaceFlinger { get; private set; }
|
internal SurfaceFlinger SurfaceFlinger { get; private set; }
|
||||||
internal AudioManager AudioManager { get; private set; }
|
internal AudioManager AudioManager { get; private set; }
|
||||||
internal AudioOutputManager AudioOutputManager { get; private set; }
|
internal AudioOutputManager AudioOutputManager { get; private set; }
|
||||||
@ -121,7 +126,11 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
public Horizon(Switch device)
|
public Horizon(Switch device)
|
||||||
{
|
{
|
||||||
|
TickSource = new TickSource(KernelConstants.CounterFrequency);
|
||||||
|
CpuEngine = new JitEngine(TickSource);
|
||||||
|
|
||||||
KernelContext = new KernelContext(
|
KernelContext = new KernelContext(
|
||||||
|
TickSource,
|
||||||
device,
|
device,
|
||||||
device.Memory,
|
device.Memory,
|
||||||
device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
|
device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
|
||||||
@ -215,40 +224,40 @@ namespace Ryujinx.HLE.HOS
|
|||||||
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
|
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
|
||||||
|
|
||||||
// First init the standard steady clock
|
// First init the standard steady clock
|
||||||
TimeServiceManager.Instance.SetupStandardSteadyClock(null, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
|
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
|
||||||
TimeServiceManager.Instance.SetupStandardLocalSystemClock(null, new SystemClockContext(), systemTime.ToSeconds());
|
TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
|
||||||
|
|
||||||
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
|
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
|
||||||
{
|
{
|
||||||
TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
|
TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
|
||||||
|
|
||||||
// The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
|
// The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
|
||||||
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(null, out SystemClockContext localSytemClockContext);
|
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
|
||||||
TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
|
TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeServiceManager.Instance.SetupStandardUserSystemClock(null, false, SteadyClockTimePoint.GetRandom());
|
TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom());
|
||||||
|
|
||||||
// FIXME: TimeZone should be init here but it's actually done in ContentManager
|
// FIXME: TimeZone should be init here but it's actually done in ContentManager
|
||||||
|
|
||||||
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
||||||
|
|
||||||
DatabaseImpl.Instance.InitializeDatabase(LibHacHorizonManager.SdbClient);
|
DatabaseImpl.Instance.InitializeDatabase(TickSource, LibHacHorizonManager.SdbClient);
|
||||||
|
|
||||||
HostSyncpoint = new NvHostSyncpt(device);
|
HostSyncpoint = new NvHostSyncpt(device);
|
||||||
|
|
||||||
SurfaceFlinger = new SurfaceFlinger(device);
|
SurfaceFlinger = new SurfaceFlinger(device);
|
||||||
|
|
||||||
InitializeAudioRenderer();
|
InitializeAudioRenderer(TickSource);
|
||||||
InitializeServices();
|
InitializeServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeAudioRenderer()
|
private void InitializeAudioRenderer(ITickSource tickSource)
|
||||||
{
|
{
|
||||||
AudioManager = new AudioManager();
|
AudioManager = new AudioManager();
|
||||||
AudioOutputManager = new AudioOutputManager();
|
AudioOutputManager = new AudioOutputManager();
|
||||||
AudioInputManager = new AudioInputManager();
|
AudioInputManager = new AudioInputManager();
|
||||||
AudioRendererManager = new AudioRendererManager();
|
AudioRendererManager = new AudioRendererManager(tickSource);
|
||||||
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
|
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
|
||||||
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
|
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
|
||||||
|
|
||||||
@ -492,12 +501,12 @@ namespace Ryujinx.HLE.HOS
|
|||||||
if (pause && !IsPaused)
|
if (pause && !IsPaused)
|
||||||
{
|
{
|
||||||
Device.AudioDeviceDriver.GetPauseEvent().Reset();
|
Device.AudioDeviceDriver.GetPauseEvent().Reset();
|
||||||
ARMeilleure.State.ExecutionContext.SuspendCounter();
|
TickSource.Suspend();
|
||||||
}
|
}
|
||||||
else if (!pause && IsPaused)
|
else if (!pause && IsPaused)
|
||||||
{
|
{
|
||||||
Device.AudioDeviceDriver.GetPauseEvent().Set();
|
Device.AudioDeviceDriver.GetPauseEvent().Set();
|
||||||
ARMeilleure.State.ExecutionContext.ResumeCounter();
|
TickSource.Resume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IsPaused = pause;
|
IsPaused = pause;
|
||||||
|
@ -12,5 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||||
public const ulong UserSlabHeapItemSize = KPageTableBase.PageSize;
|
public const ulong UserSlabHeapItemSize = KPageTableBase.PageSize;
|
||||||
public const ulong UserSlabHeapSize = 0x3de000;
|
public const ulong UserSlabHeapSize = 0x3de000;
|
||||||
|
|
||||||
|
public const ulong CounterFrequency = 19200000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
|
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
|
||||||
@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
|
|
||||||
public Switch Device { get; }
|
public Switch Device { get; }
|
||||||
public MemoryBlock Memory { get; }
|
public MemoryBlock Memory { get; }
|
||||||
|
public ITickSource TickSource { get; }
|
||||||
public Syscall Syscall { get; }
|
public Syscall Syscall { get; }
|
||||||
public SyscallHandler SyscallHandler { get; }
|
public SyscallHandler SyscallHandler { get; }
|
||||||
|
|
||||||
@ -52,11 +54,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
private ulong _threadUid;
|
private ulong _threadUid;
|
||||||
|
|
||||||
public KernelContext(
|
public KernelContext(
|
||||||
|
ITickSource tickSource,
|
||||||
Switch device,
|
Switch device,
|
||||||
MemoryBlock memory,
|
MemoryBlock memory,
|
||||||
MemorySize memorySize,
|
MemorySize memorySize,
|
||||||
MemoryArrange memoryArrange)
|
MemoryArrange memoryArrange)
|
||||||
{
|
{
|
||||||
|
TickSource = tickSource;
|
||||||
Device = device;
|
Device = device;
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|
||||||
{
|
|
||||||
class KMemoryRegionBlock
|
|
||||||
{
|
|
||||||
public long[][] Masks;
|
|
||||||
|
|
||||||
public ulong FreeCount;
|
|
||||||
public int MaxLevel;
|
|
||||||
public ulong StartAligned;
|
|
||||||
public ulong SizeInBlocksTruncated;
|
|
||||||
public ulong SizeInBlocksRounded;
|
|
||||||
public int Order;
|
|
||||||
public int NextOrder;
|
|
||||||
|
|
||||||
public bool TryCoalesce(int index, int count)
|
|
||||||
{
|
|
||||||
long mask = ((1L << count) - 1) << (index & 63);
|
|
||||||
|
|
||||||
index /= 64;
|
|
||||||
|
|
||||||
if (count >= 64)
|
|
||||||
{
|
|
||||||
int remaining = count;
|
|
||||||
int tempIdx = index;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (Masks[MaxLevel - 1][tempIdx++] != -1L)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
remaining -= 64;
|
|
||||||
}
|
|
||||||
while (remaining != 0);
|
|
||||||
|
|
||||||
remaining = count;
|
|
||||||
tempIdx = index;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
Masks[MaxLevel - 1][tempIdx] = 0;
|
|
||||||
|
|
||||||
ClearMaskBit(MaxLevel - 2, tempIdx++);
|
|
||||||
|
|
||||||
remaining -= 64;
|
|
||||||
}
|
|
||||||
while (remaining != 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
long value = Masks[MaxLevel - 1][index];
|
|
||||||
|
|
||||||
if ((mask & ~value) != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value &= ~mask;
|
|
||||||
|
|
||||||
Masks[MaxLevel - 1][index] = value;
|
|
||||||
|
|
||||||
if (value == 0)
|
|
||||||
{
|
|
||||||
ClearMaskBit(MaxLevel - 2, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeCount -= (ulong)count;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearMaskBit(int startLevel, int index)
|
|
||||||
{
|
|
||||||
for (int level = startLevel; level >= 0; level--, index /= 64)
|
|
||||||
{
|
|
||||||
Masks[level][index / 64] &= ~(1L << (index & 63));
|
|
||||||
|
|
||||||
if (Masks[level][index / 64] != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,102 +1,42 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryRegionManager
|
class KMemoryRegionManager
|
||||||
{
|
{
|
||||||
private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
|
private readonly KPageHeap _pageHeap;
|
||||||
|
|
||||||
public ulong Address { get; private set; }
|
public ulong Address { get; }
|
||||||
public ulong EndAddr { get; private set; }
|
public ulong Size { get; }
|
||||||
public ulong Size { get; private set; }
|
public ulong EndAddr => Address + Size;
|
||||||
|
|
||||||
private int _blockOrdersCount;
|
|
||||||
|
|
||||||
private readonly KMemoryRegionBlock[] _blocks;
|
|
||||||
|
|
||||||
private readonly ushort[] _pageReferenceCounts;
|
private readonly ushort[] _pageReferenceCounts;
|
||||||
|
|
||||||
public KMemoryRegionManager(ulong address, ulong size, ulong endAddr)
|
public KMemoryRegionManager(ulong address, ulong size, ulong endAddr)
|
||||||
{
|
{
|
||||||
_blocks = new KMemoryRegionBlock[BlockOrders.Length];
|
|
||||||
|
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
EndAddr = endAddr;
|
|
||||||
|
|
||||||
_blockOrdersCount = BlockOrders.Length;
|
|
||||||
|
|
||||||
for (int blockIndex = 0; blockIndex < _blockOrdersCount; blockIndex++)
|
|
||||||
{
|
|
||||||
_blocks[blockIndex] = new KMemoryRegionBlock();
|
|
||||||
|
|
||||||
_blocks[blockIndex].Order = BlockOrders[blockIndex];
|
|
||||||
|
|
||||||
int nextOrder = blockIndex == _blockOrdersCount - 1 ? 0 : BlockOrders[blockIndex + 1];
|
|
||||||
|
|
||||||
_blocks[blockIndex].NextOrder = nextOrder;
|
|
||||||
|
|
||||||
int currBlockSize = 1 << BlockOrders[blockIndex];
|
|
||||||
int nextBlockSize = currBlockSize;
|
|
||||||
|
|
||||||
if (nextOrder != 0)
|
|
||||||
{
|
|
||||||
nextBlockSize = 1 << nextOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong startAligned = BitUtils.AlignDown(address, nextBlockSize);
|
|
||||||
ulong endAddrAligned = BitUtils.AlignDown(endAddr, currBlockSize);
|
|
||||||
|
|
||||||
ulong sizeInBlocksTruncated = (endAddrAligned - startAligned) >> BlockOrders[blockIndex];
|
|
||||||
|
|
||||||
ulong endAddrRounded = BitUtils.AlignUp(address + size, nextBlockSize);
|
|
||||||
|
|
||||||
ulong sizeInBlocksRounded = (endAddrRounded - startAligned) >> BlockOrders[blockIndex];
|
|
||||||
|
|
||||||
_blocks[blockIndex].StartAligned = startAligned;
|
|
||||||
_blocks[blockIndex].SizeInBlocksTruncated = sizeInBlocksTruncated;
|
|
||||||
_blocks[blockIndex].SizeInBlocksRounded = sizeInBlocksRounded;
|
|
||||||
|
|
||||||
ulong currSizeInBlocks = sizeInBlocksRounded;
|
|
||||||
|
|
||||||
int maxLevel = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
maxLevel++;
|
|
||||||
}
|
|
||||||
while ((currSizeInBlocks /= 64) != 0);
|
|
||||||
|
|
||||||
_blocks[blockIndex].MaxLevel = maxLevel;
|
|
||||||
|
|
||||||
_blocks[blockIndex].Masks = new long[maxLevel][];
|
|
||||||
|
|
||||||
currSizeInBlocks = sizeInBlocksRounded;
|
|
||||||
|
|
||||||
for (int level = maxLevel - 1; level >= 0; level--)
|
|
||||||
{
|
|
||||||
currSizeInBlocks = (currSizeInBlocks + 63) / 64;
|
|
||||||
|
|
||||||
_blocks[blockIndex].Masks[level] = new long[currSizeInBlocks];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_pageReferenceCounts = new ushort[size / KPageTableBase.PageSize];
|
_pageReferenceCounts = new ushort[size / KPageTableBase.PageSize];
|
||||||
|
|
||||||
if (size != 0)
|
_pageHeap = new KPageHeap(address, size);
|
||||||
{
|
_pageHeap.Free(address, size / KPageTableBase.PageSize);
|
||||||
FreePages(address, size / KPageTableBase.PageSize);
|
_pageHeap.UpdateUsedSize();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult AllocatePages(ulong pagesCount, bool backwards, out KPageList pageList)
|
public KernelResult AllocatePages(out KPageList pageList, ulong pagesCount)
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
if (pagesCount == 0)
|
||||||
{
|
{
|
||||||
KernelResult result = AllocatePagesImpl(pagesCount, backwards, out pageList);
|
pageList = new KPageList();
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_pageHeap)
|
||||||
|
{
|
||||||
|
KernelResult result = AllocatePagesImpl(out pageList, pagesCount, false);
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
@ -112,9 +52,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
public ulong AllocatePagesContiguous(KernelContext context, ulong pagesCount, bool backwards)
|
public ulong AllocatePagesContiguous(KernelContext context, ulong pagesCount, bool backwards)
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
if (pagesCount == 0)
|
||||||
{
|
{
|
||||||
ulong address = AllocatePagesContiguousImpl(pagesCount, backwards);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_pageHeap)
|
||||||
|
{
|
||||||
|
ulong address = AllocatePagesContiguousImpl(pagesCount, 1, backwards);
|
||||||
|
|
||||||
if (address != 0)
|
if (address != 0)
|
||||||
{
|
{
|
||||||
@ -126,373 +71,110 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult AllocatePagesImpl(ulong pagesCount, bool backwards, out KPageList pageList)
|
private KernelResult AllocatePagesImpl(out KPageList pageList, ulong pagesCount, bool random)
|
||||||
{
|
{
|
||||||
pageList = new KPageList();
|
pageList = new KPageList();
|
||||||
|
|
||||||
if (_blockOrdersCount > 0)
|
int heapIndex = KPageHeap.GetBlockIndex(pagesCount);
|
||||||
{
|
|
||||||
if (GetFreePagesImpl() < pagesCount)
|
if (heapIndex < 0)
|
||||||
{
|
|
||||||
return KernelResult.OutOfMemory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pagesCount != 0)
|
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int blockIndex = _blockOrdersCount - 1; blockIndex >= 0; blockIndex--)
|
for (int index = heapIndex; index >= 0; index--)
|
||||||
{
|
{
|
||||||
KMemoryRegionBlock block = _blocks[blockIndex];
|
ulong pagesPerAlloc = KPageHeap.GetBlockPagesCount(index);
|
||||||
|
|
||||||
ulong bestFitBlockSize = 1UL << block.Order;
|
while (pagesCount >= pagesPerAlloc)
|
||||||
|
|
||||||
ulong blockPagesCount = bestFitBlockSize / KPageTableBase.PageSize;
|
|
||||||
|
|
||||||
// Check if this is the best fit for this page size.
|
|
||||||
// If so, try allocating as much requested pages as possible.
|
|
||||||
while (blockPagesCount <= pagesCount)
|
|
||||||
{
|
{
|
||||||
ulong address = AllocatePagesForOrder(blockIndex, backwards, bestFitBlockSize);
|
ulong allocatedBlock = _pageHeap.AllocateBlock(index, random);
|
||||||
|
|
||||||
// The address being zero means that no free space was found on that order,
|
if (allocatedBlock == 0)
|
||||||
// just give up and try with the next one.
|
|
||||||
if (address == 0)
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new allocated page(s) to the pages list.
|
KernelResult result = pageList.AddRange(allocatedBlock, pagesPerAlloc);
|
||||||
// If an error occurs, then free all allocated pages and fail.
|
|
||||||
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
FreePages(address, blockPagesCount);
|
FreePages(pageList);
|
||||||
|
_pageHeap.Free(allocatedBlock, pagesPerAlloc);
|
||||||
foreach (KPageNode pageNode in pageList)
|
|
||||||
{
|
|
||||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pagesCount -= blockPagesCount;
|
pagesCount -= pagesPerAlloc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success case, all requested pages were allocated successfully.
|
if (pagesCount != 0)
|
||||||
if (pagesCount == 0)
|
|
||||||
{
|
{
|
||||||
return KernelResult.Success;
|
FreePages(pageList);
|
||||||
|
|
||||||
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error case, free allocated pages and return out of memory.
|
return KernelResult.Success;
|
||||||
foreach (KPageNode pageNode in pageList)
|
|
||||||
{
|
|
||||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
pageList = null;
|
|
||||||
|
|
||||||
return KernelResult.OutOfMemory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong AllocatePagesContiguousImpl(ulong pagesCount, bool backwards)
|
private ulong AllocatePagesContiguousImpl(ulong pagesCount, ulong alignPages, bool random)
|
||||||
{
|
{
|
||||||
if (pagesCount == 0 || _blocks.Length < 1)
|
int heapIndex = KPageHeap.GetAlignedBlockIndex(pagesCount, alignPages);
|
||||||
|
|
||||||
|
ulong allocatedBlock = _pageHeap.AllocateBlock(heapIndex, random);
|
||||||
|
|
||||||
|
if (allocatedBlock == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int blockIndex = 0;
|
ulong allocatedPages = KPageHeap.GetBlockPagesCount(heapIndex);
|
||||||
|
|
||||||
while ((1UL << _blocks[blockIndex].Order) / KPageTableBase.PageSize < pagesCount)
|
if (allocatedPages > pagesCount)
|
||||||
{
|
{
|
||||||
if (++blockIndex >= _blocks.Length)
|
_pageHeap.Free(allocatedBlock + pagesCount * KPageTableBase.PageSize, allocatedPages - pagesCount);
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong tightestFitBlockSize = 1UL << _blocks[blockIndex].Order;
|
return allocatedBlock;
|
||||||
|
|
||||||
ulong address = AllocatePagesForOrder(blockIndex, backwards, tightestFitBlockSize);
|
|
||||||
|
|
||||||
ulong requiredSize = pagesCount * KPageTableBase.PageSize;
|
|
||||||
|
|
||||||
if (address != 0 && tightestFitBlockSize > requiredSize)
|
|
||||||
{
|
|
||||||
FreePages(address + requiredSize, (tightestFitBlockSize - requiredSize) / KPageTableBase.PageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong AllocatePagesForOrder(int blockIndex, bool backwards, ulong bestFitBlockSize)
|
public void FreePage(ulong address)
|
||||||
{
|
{
|
||||||
ulong address = 0;
|
lock (_pageHeap)
|
||||||
|
|
||||||
KMemoryRegionBlock block = null;
|
|
||||||
|
|
||||||
for (int currBlockIndex = blockIndex;
|
|
||||||
currBlockIndex < _blockOrdersCount && address == 0;
|
|
||||||
currBlockIndex++)
|
|
||||||
{
|
{
|
||||||
block = _blocks[currBlockIndex];
|
_pageHeap.Free(address, 1);
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
bool zeroMask = false;
|
|
||||||
|
|
||||||
for (int level = 0; level < block.MaxLevel; level++)
|
|
||||||
{
|
|
||||||
long mask = block.Masks[level][index];
|
|
||||||
|
|
||||||
if (mask == 0)
|
|
||||||
{
|
|
||||||
zeroMask = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backwards)
|
|
||||||
{
|
|
||||||
index = (index * 64 + 63) - BitOperations.LeadingZeroCount((ulong)mask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index = index * 64 + BitOperations.LeadingZeroCount((ulong)BitUtils.ReverseBits64(mask));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
block.FreeCount--;
|
|
||||||
|
|
||||||
int tempIdx = index;
|
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
|
||||||
{
|
|
||||||
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
|
||||||
|
|
||||||
if (block.Masks[level][tempIdx / 64] != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address = block.StartAligned + ((ulong)index << block.Order);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int currBlockIndex = blockIndex;
|
|
||||||
currBlockIndex < _blockOrdersCount && address == 0;
|
|
||||||
currBlockIndex++)
|
|
||||||
{
|
|
||||||
block = _blocks[currBlockIndex];
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
bool zeroMask = false;
|
|
||||||
|
|
||||||
for (int level = 0; level < block.MaxLevel; level++)
|
|
||||||
{
|
|
||||||
long mask = block.Masks[level][index];
|
|
||||||
|
|
||||||
if (mask == 0)
|
|
||||||
{
|
|
||||||
zeroMask = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backwards)
|
|
||||||
{
|
|
||||||
index = index * 64 + BitOperations.LeadingZeroCount((ulong)BitUtils.ReverseBits64(mask));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index = (index * 64 + 63) - BitOperations.LeadingZeroCount((ulong)mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
block.FreeCount--;
|
|
||||||
|
|
||||||
int tempIdx = index;
|
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
|
||||||
{
|
|
||||||
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
|
||||||
|
|
||||||
if (block.Masks[level][tempIdx / 64] != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address = block.StartAligned + ((ulong)index << block.Order);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address != 0)
|
|
||||||
{
|
|
||||||
// If we are using a larger order than best fit, then we should
|
|
||||||
// split it into smaller blocks.
|
|
||||||
ulong firstFreeBlockSize = 1UL << block.Order;
|
|
||||||
|
|
||||||
if (firstFreeBlockSize > bestFitBlockSize)
|
|
||||||
{
|
|
||||||
FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KPageTableBase.PageSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FreePages(ulong address, ulong pagesCount)
|
public void FreePages(KPageList pageList)
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
lock (_pageHeap)
|
||||||
{
|
{
|
||||||
ulong endAddr = address + pagesCount * KPageTableBase.PageSize;
|
foreach (KPageNode pageNode in pageList)
|
||||||
|
|
||||||
int blockIndex = _blockOrdersCount - 1;
|
|
||||||
|
|
||||||
ulong addressRounded = 0;
|
|
||||||
ulong endAddrTruncated = 0;
|
|
||||||
|
|
||||||
for (; blockIndex >= 0; blockIndex--)
|
|
||||||
{
|
{
|
||||||
KMemoryRegionBlock allocInfo = _blocks[blockIndex];
|
_pageHeap.Free(pageNode.Address, pageNode.PagesCount);
|
||||||
|
|
||||||
int blockSize = 1 << allocInfo.Order;
|
|
||||||
|
|
||||||
addressRounded = BitUtils.AlignUp (address, blockSize);
|
|
||||||
endAddrTruncated = BitUtils.AlignDown(endAddr, blockSize);
|
|
||||||
|
|
||||||
if (addressRounded < endAddrTruncated)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FreeRegion(ulong currAddress)
|
public void FreePages(ulong address, ulong pagesCount)
|
||||||
{
|
{
|
||||||
for (int currBlockIndex = blockIndex;
|
lock (_pageHeap)
|
||||||
currBlockIndex < _blockOrdersCount && currAddress != 0;
|
{
|
||||||
currBlockIndex++)
|
_pageHeap.Free(address, pagesCount);
|
||||||
{
|
|
||||||
KMemoryRegionBlock block = _blocks[currBlockIndex];
|
|
||||||
|
|
||||||
block.FreeCount++;
|
|
||||||
|
|
||||||
ulong freedBlocks = (currAddress - block.StartAligned) >> block.Order;
|
|
||||||
|
|
||||||
int index = (int)freedBlocks;
|
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, index /= 64)
|
|
||||||
{
|
|
||||||
long mask = block.Masks[level][index / 64];
|
|
||||||
|
|
||||||
block.Masks[level][index / 64] = mask | (1L << (index & 63));
|
|
||||||
|
|
||||||
if (mask != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int blockSizeDelta = 1 << (block.NextOrder - block.Order);
|
|
||||||
|
|
||||||
int freedBlocksTruncated = BitUtils.AlignDown((int)freedBlocks, blockSizeDelta);
|
|
||||||
|
|
||||||
if (!block.TryCoalesce(freedBlocksTruncated, blockSizeDelta))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currAddress = block.StartAligned + ((ulong)freedBlocksTruncated << block.Order);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free inside aligned region.
|
|
||||||
ulong baseAddress = addressRounded;
|
|
||||||
|
|
||||||
while (baseAddress < endAddrTruncated)
|
|
||||||
{
|
|
||||||
ulong blockSize = 1UL << _blocks[blockIndex].Order;
|
|
||||||
|
|
||||||
FreeRegion(baseAddress);
|
|
||||||
|
|
||||||
baseAddress += blockSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nextBlockIndex = blockIndex - 1;
|
|
||||||
|
|
||||||
// Free region between Address and aligned region start.
|
|
||||||
baseAddress = addressRounded;
|
|
||||||
|
|
||||||
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
|
||||||
{
|
|
||||||
ulong blockSize = 1UL << _blocks[blockIndex].Order;
|
|
||||||
|
|
||||||
while (baseAddress - blockSize >= address)
|
|
||||||
{
|
|
||||||
baseAddress -= blockSize;
|
|
||||||
|
|
||||||
FreeRegion(baseAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free region between aligned region end and End Address.
|
|
||||||
baseAddress = endAddrTruncated;
|
|
||||||
|
|
||||||
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
|
||||||
{
|
|
||||||
ulong blockSize = 1UL << _blocks[blockIndex].Order;
|
|
||||||
|
|
||||||
while (baseAddress + blockSize <= endAddr)
|
|
||||||
{
|
|
||||||
FreeRegion(baseAddress);
|
|
||||||
|
|
||||||
baseAddress += blockSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetFreePages()
|
public ulong GetFreePages()
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
lock (_pageHeap)
|
||||||
{
|
{
|
||||||
return GetFreePagesImpl();
|
return _pageHeap.GetFreePagesCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong GetFreePagesImpl()
|
|
||||||
{
|
|
||||||
ulong availablePages = 0;
|
|
||||||
|
|
||||||
for (int blockIndex = 0; blockIndex < _blockOrdersCount; blockIndex++)
|
|
||||||
{
|
|
||||||
KMemoryRegionBlock block = _blocks[blockIndex];
|
|
||||||
|
|
||||||
ulong blockPagesCount = (1UL << block.Order) / KPageTableBase.PageSize;
|
|
||||||
|
|
||||||
availablePages += blockPagesCount * block.FreeCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return availablePages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncrementPagesReferenceCount(ulong address, ulong pagesCount)
|
public void IncrementPagesReferenceCount(ulong address, ulong pagesCount)
|
||||||
{
|
{
|
||||||
ulong index = GetPageOffset(address);
|
ulong index = GetPageOffset(address);
|
||||||
|
298
Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs
Normal file
298
Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
{
|
||||||
|
class KPageBitmap
|
||||||
|
{
|
||||||
|
private struct RandomNumberGenerator
|
||||||
|
{
|
||||||
|
private uint _entropy;
|
||||||
|
private uint _bitsAvailable;
|
||||||
|
|
||||||
|
private void RefreshEntropy()
|
||||||
|
{
|
||||||
|
_entropy = 0;
|
||||||
|
_bitsAvailable = sizeof(uint) * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GenerateRandomBit()
|
||||||
|
{
|
||||||
|
if (_bitsAvailable == 0)
|
||||||
|
{
|
||||||
|
RefreshEntropy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bit = (_entropy & 1) != 0;
|
||||||
|
|
||||||
|
_entropy >>= 1;
|
||||||
|
_bitsAvailable--;
|
||||||
|
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SelectRandomBit(ulong bitmap)
|
||||||
|
{
|
||||||
|
int selected = 0;
|
||||||
|
|
||||||
|
int bitsCount = UInt64BitSize / 2;
|
||||||
|
ulong mask = (1UL << bitsCount) - 1;
|
||||||
|
|
||||||
|
while (bitsCount != 0)
|
||||||
|
{
|
||||||
|
ulong low = bitmap & mask;
|
||||||
|
ulong high = (bitmap >> bitsCount) & mask;
|
||||||
|
|
||||||
|
bool chooseLow;
|
||||||
|
|
||||||
|
if (high == 0)
|
||||||
|
{
|
||||||
|
chooseLow = true;
|
||||||
|
}
|
||||||
|
else if (low == 0)
|
||||||
|
{
|
||||||
|
chooseLow = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chooseLow = GenerateRandomBit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chooseLow)
|
||||||
|
{
|
||||||
|
bitmap = low;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bitmap = high;
|
||||||
|
selected += bitsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitsCount /= 2;
|
||||||
|
mask >>= bitsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int UInt64BitSize = sizeof(ulong) * 8;
|
||||||
|
private const int MaxDepth = 4;
|
||||||
|
|
||||||
|
private readonly RandomNumberGenerator _rng;
|
||||||
|
private readonly ArraySegment<ulong>[] _bitStorages;
|
||||||
|
private int _usedDepths;
|
||||||
|
|
||||||
|
public int BitsCount { get; private set; }
|
||||||
|
|
||||||
|
public int HighestDepthIndex => _usedDepths - 1;
|
||||||
|
|
||||||
|
public KPageBitmap()
|
||||||
|
{
|
||||||
|
_rng = new RandomNumberGenerator();
|
||||||
|
_bitStorages = new ArraySegment<ulong>[MaxDepth];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArraySegment<ulong> Initialize(ArraySegment<ulong> storage, ulong size)
|
||||||
|
{
|
||||||
|
_usedDepths = GetRequiredDepth(size);
|
||||||
|
|
||||||
|
for (int depth = HighestDepthIndex; depth >= 0; depth--)
|
||||||
|
{
|
||||||
|
_bitStorages[depth] = storage;
|
||||||
|
size = BitUtils.DivRoundUp(size, UInt64BitSize);
|
||||||
|
storage = storage.Slice((int)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong FindFreeBlock(bool random)
|
||||||
|
{
|
||||||
|
ulong offset = 0;
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
if (random)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ulong v = _bitStorages[depth][(int)offset];
|
||||||
|
|
||||||
|
if (v == 0)
|
||||||
|
{
|
||||||
|
return ulong.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = offset * UInt64BitSize + (ulong)_rng.SelectRandomBit(v);
|
||||||
|
}
|
||||||
|
while (++depth < _usedDepths);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ulong v = _bitStorages[depth][(int)offset];
|
||||||
|
|
||||||
|
if (v == 0)
|
||||||
|
{
|
||||||
|
return ulong.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = offset * UInt64BitSize + (ulong)BitOperations.TrailingZeroCount(v);
|
||||||
|
}
|
||||||
|
while (++depth < _usedDepths);
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBit(ulong offset)
|
||||||
|
{
|
||||||
|
SetBit(HighestDepthIndex, offset);
|
||||||
|
BitsCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearBit(ulong offset)
|
||||||
|
{
|
||||||
|
ClearBit(HighestDepthIndex, offset);
|
||||||
|
BitsCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClearRange(ulong offset, int count)
|
||||||
|
{
|
||||||
|
int depth = HighestDepthIndex;
|
||||||
|
var bits = _bitStorages[depth];
|
||||||
|
|
||||||
|
int bitInd = (int)(offset / UInt64BitSize);
|
||||||
|
|
||||||
|
if (count < UInt64BitSize)
|
||||||
|
{
|
||||||
|
int shift = (int)(offset % UInt64BitSize);
|
||||||
|
|
||||||
|
ulong mask = ((1UL << count) - 1) << shift;
|
||||||
|
|
||||||
|
ulong v = bits[bitInd];
|
||||||
|
|
||||||
|
if ((v & mask) != mask)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
v &= ~mask;
|
||||||
|
bits[bitInd] = v;
|
||||||
|
|
||||||
|
if (v == 0)
|
||||||
|
{
|
||||||
|
ClearBit(depth - 1, (ulong)bitInd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int remaining = count;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (bits[bitInd + i++] != ulong.MaxValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining -= UInt64BitSize;
|
||||||
|
}
|
||||||
|
while (remaining > 0);
|
||||||
|
|
||||||
|
remaining = count;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bits[bitInd + i] = 0;
|
||||||
|
ClearBit(depth - 1, (ulong)(bitInd + i));
|
||||||
|
i++;
|
||||||
|
remaining -= UInt64BitSize;
|
||||||
|
}
|
||||||
|
while (remaining > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitsCount -= count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetBit(int depth, ulong offset)
|
||||||
|
{
|
||||||
|
while (depth >= 0)
|
||||||
|
{
|
||||||
|
int ind = (int)(offset / UInt64BitSize);
|
||||||
|
int which = (int)(offset % UInt64BitSize);
|
||||||
|
|
||||||
|
ulong mask = 1UL << which;
|
||||||
|
|
||||||
|
ulong v = _bitStorages[depth][ind];
|
||||||
|
|
||||||
|
_bitStorages[depth][ind] = v | mask;
|
||||||
|
|
||||||
|
if (v != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = (ulong)ind;
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearBit(int depth, ulong offset)
|
||||||
|
{
|
||||||
|
while (depth >= 0)
|
||||||
|
{
|
||||||
|
int ind = (int)(offset / UInt64BitSize);
|
||||||
|
int which = (int)(offset % UInt64BitSize);
|
||||||
|
|
||||||
|
ulong mask = 1UL << which;
|
||||||
|
|
||||||
|
ulong v = _bitStorages[depth][ind];
|
||||||
|
|
||||||
|
v &= ~mask;
|
||||||
|
|
||||||
|
_bitStorages[depth][ind] = v;
|
||||||
|
|
||||||
|
if (v != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = (ulong)ind;
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetRequiredDepth(ulong regionSize)
|
||||||
|
{
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
regionSize /= UInt64BitSize;
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
while (regionSize != 0);
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CalculateManagementOverheadSize(ulong regionSize)
|
||||||
|
{
|
||||||
|
int overheadBits = 0;
|
||||||
|
|
||||||
|
for (int depth = GetRequiredDepth(regionSize) - 1; depth >= 0; depth--)
|
||||||
|
{
|
||||||
|
regionSize = BitUtils.DivRoundUp(regionSize, UInt64BitSize);
|
||||||
|
overheadBits += (int)regionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return overheadBits * sizeof(ulong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
283
Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs
Normal file
283
Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
{
|
||||||
|
class KPageHeap
|
||||||
|
{
|
||||||
|
private class Block
|
||||||
|
{
|
||||||
|
private KPageBitmap _bitmap = new KPageBitmap();
|
||||||
|
private ulong _heapAddress;
|
||||||
|
private ulong _endOffset;
|
||||||
|
|
||||||
|
public int Shift { get; private set; }
|
||||||
|
public int NextShift { get; private set; }
|
||||||
|
public ulong Size => 1UL << Shift;
|
||||||
|
public int PagesCount => (int)(Size / KPageTableBase.PageSize);
|
||||||
|
public int FreeBlocksCount => _bitmap.BitsCount;
|
||||||
|
public int FreePagesCount => FreeBlocksCount * PagesCount;
|
||||||
|
|
||||||
|
public ArraySegment<ulong> Initialize(ulong address, ulong size, int blockShift, int nextBlockShift, ArraySegment<ulong> bitStorage)
|
||||||
|
{
|
||||||
|
Shift = blockShift;
|
||||||
|
NextShift = nextBlockShift;
|
||||||
|
|
||||||
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
|
ulong align = nextBlockShift != 0
|
||||||
|
? 1UL << nextBlockShift
|
||||||
|
: 1UL << blockShift;
|
||||||
|
|
||||||
|
address = BitUtils.AlignDown(address, align);
|
||||||
|
endAddress = BitUtils.AlignUp (endAddress, align);
|
||||||
|
|
||||||
|
_heapAddress = address;
|
||||||
|
_endOffset = (endAddress - address) / (1UL << blockShift);
|
||||||
|
|
||||||
|
return _bitmap.Initialize(bitStorage, _endOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong PushBlock(ulong address)
|
||||||
|
{
|
||||||
|
ulong offset = (address - _heapAddress) >> Shift;
|
||||||
|
|
||||||
|
_bitmap.SetBit(offset);
|
||||||
|
|
||||||
|
if (NextShift != 0)
|
||||||
|
{
|
||||||
|
int diff = 1 << (NextShift - Shift);
|
||||||
|
|
||||||
|
offset = BitUtils.AlignDown(offset, diff);
|
||||||
|
|
||||||
|
if (_bitmap.ClearRange(offset, diff))
|
||||||
|
{
|
||||||
|
return _heapAddress + (offset << Shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong PopBlock(bool random)
|
||||||
|
{
|
||||||
|
long sOffset = (long)_bitmap.FindFreeBlock(random);
|
||||||
|
|
||||||
|
if (sOffset < 0L)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong offset = (ulong)sOffset;
|
||||||
|
|
||||||
|
_bitmap.ClearBit(offset);
|
||||||
|
|
||||||
|
return _heapAddress + (offset << Shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CalculateManagementOverheadSize(ulong regionSize, int currBlockShift, int nextBlockShift)
|
||||||
|
{
|
||||||
|
ulong currBlockSize = 1UL << currBlockShift;
|
||||||
|
ulong nextBlockSize = 1UL << nextBlockShift;
|
||||||
|
ulong align = nextBlockShift != 0 ? nextBlockSize : currBlockSize;
|
||||||
|
return KPageBitmap.CalculateManagementOverheadSize((align * 2 + BitUtils.AlignUp(regionSize, align)) / currBlockSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly int[] _memoryBlockPageShifts = new int[] { 12, 16, 21, 22, 25, 29, 30 };
|
||||||
|
|
||||||
|
private readonly ulong _heapAddress;
|
||||||
|
private readonly ulong _heapSize;
|
||||||
|
private ulong _usedSize;
|
||||||
|
private readonly int _blocksCount;
|
||||||
|
private readonly Block[] _blocks;
|
||||||
|
|
||||||
|
public KPageHeap(ulong address, ulong size) : this(address, size, _memoryBlockPageShifts)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public KPageHeap(ulong address, ulong size, int[] blockShifts)
|
||||||
|
{
|
||||||
|
_heapAddress = address;
|
||||||
|
_heapSize = size;
|
||||||
|
_blocksCount = blockShifts.Length;
|
||||||
|
_blocks = new Block[_memoryBlockPageShifts.Length];
|
||||||
|
|
||||||
|
var currBitmapStorage = new ArraySegment<ulong>(new ulong[CalculateManagementOverheadSize(size, blockShifts)]);
|
||||||
|
|
||||||
|
for (int i = 0; i < blockShifts.Length; i++)
|
||||||
|
{
|
||||||
|
int currBlockShift = blockShifts[i];
|
||||||
|
int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
|
||||||
|
|
||||||
|
_blocks[i] = new Block();
|
||||||
|
|
||||||
|
currBitmapStorage = _blocks[i].Initialize(address, size, currBlockShift, nextBlockShift, currBitmapStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUsedSize()
|
||||||
|
{
|
||||||
|
_usedSize = _heapSize - (GetFreePagesCount() * KPageTableBase.PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GetFreePagesCount()
|
||||||
|
{
|
||||||
|
ulong freeCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < _blocksCount; i++)
|
||||||
|
{
|
||||||
|
freeCount += (ulong)_blocks[i].FreePagesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong AllocateBlock(int index, bool random)
|
||||||
|
{
|
||||||
|
ulong neededSize = _blocks[index].Size;
|
||||||
|
|
||||||
|
for (int i = index; i < _blocksCount; i++)
|
||||||
|
{
|
||||||
|
ulong address = _blocks[i].PopBlock(random);
|
||||||
|
|
||||||
|
if (address != 0)
|
||||||
|
{
|
||||||
|
ulong allocatedSize = _blocks[i].Size;
|
||||||
|
|
||||||
|
if (allocatedSize > neededSize)
|
||||||
|
{
|
||||||
|
Free(address + neededSize, (allocatedSize - neededSize) / KPageTableBase.PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FreeBlock(ulong block, int index)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
block = _blocks[index++].PushBlock(block);
|
||||||
|
}
|
||||||
|
while (block != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Free(ulong address, ulong pagesCount)
|
||||||
|
{
|
||||||
|
if (pagesCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bigIndex = _blocksCount - 1;
|
||||||
|
|
||||||
|
ulong start = address;
|
||||||
|
ulong end = address + pagesCount * KPageTableBase.PageSize;
|
||||||
|
ulong beforeStart = start;
|
||||||
|
ulong beforeEnd = start;
|
||||||
|
ulong afterStart = end;
|
||||||
|
ulong afterEnd = end;
|
||||||
|
|
||||||
|
while (bigIndex >= 0)
|
||||||
|
{
|
||||||
|
ulong blockSize = _blocks[bigIndex].Size;
|
||||||
|
|
||||||
|
ulong bigStart = BitUtils.AlignUp (start, blockSize);
|
||||||
|
ulong bigEnd = BitUtils.AlignDown(end, blockSize);
|
||||||
|
|
||||||
|
if (bigStart < bigEnd)
|
||||||
|
{
|
||||||
|
for (ulong block = bigStart; block < bigEnd; block += blockSize)
|
||||||
|
{
|
||||||
|
FreeBlock(block, bigIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEnd = bigStart;
|
||||||
|
afterStart = bigEnd;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = bigIndex - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
ulong blockSize = _blocks[i].Size;
|
||||||
|
|
||||||
|
while (beforeStart + blockSize <= beforeEnd)
|
||||||
|
{
|
||||||
|
beforeEnd -= blockSize;
|
||||||
|
FreeBlock(beforeEnd, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = bigIndex - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
ulong blockSize = _blocks[i].Size;
|
||||||
|
|
||||||
|
while (afterStart + blockSize <= afterEnd)
|
||||||
|
{
|
||||||
|
FreeBlock(afterStart, i);
|
||||||
|
afterStart += blockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetAlignedBlockIndex(ulong pagesCount, ulong alignPages)
|
||||||
|
{
|
||||||
|
ulong targetPages = Math.Max(pagesCount, alignPages);
|
||||||
|
|
||||||
|
for (int i = 0; i < _memoryBlockPageShifts.Length; i++)
|
||||||
|
{
|
||||||
|
if (targetPages <= GetBlockPagesCount(i))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetBlockIndex(ulong pagesCount)
|
||||||
|
{
|
||||||
|
for (int i = _memoryBlockPageShifts.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (pagesCount >= GetBlockPagesCount(i))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong GetBlockSize(int index)
|
||||||
|
{
|
||||||
|
return 1UL << _memoryBlockPageShifts[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong GetBlockPagesCount(int index)
|
||||||
|
{
|
||||||
|
return GetBlockSize(index) / KPageTableBase.PageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CalculateManagementOverheadSize(ulong regionSize, int[] blockShifts)
|
||||||
|
{
|
||||||
|
int overheadSize = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < blockShifts.Length; i++)
|
||||||
|
{
|
||||||
|
int currBlockShift = blockShifts[i];
|
||||||
|
int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
|
||||||
|
overheadSize += Block.CalculateManagementOverheadSize(regionSize, currBlockShift, nextBlockShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BitUtils.AlignUp(overheadSize, KPageTableBase.PageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -555,7 +555,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
KMemoryRegionManager region = GetMemoryRegionManager();
|
KMemoryRegionManager region = GetMemoryRegionManager();
|
||||||
|
|
||||||
KernelResult result = region.AllocatePages(pagesCount, _aslrDisabled, out KPageList pageList);
|
KernelResult result = region.AllocatePages(out KPageList pageList, pagesCount);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
@ -712,7 +712,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
KMemoryRegionManager region = GetMemoryRegionManager();
|
KMemoryRegionManager region = GetMemoryRegionManager();
|
||||||
|
|
||||||
KernelResult result = region.AllocatePages(pagesCount, _aslrDisabled, out KPageList pageList);
|
KernelResult result = region.AllocatePages(out KPageList pageList, pagesCount);
|
||||||
|
|
||||||
using var _ = new OnScopeExit(() => pageList.DecrementPagesReferenceCount(Context.MemoryManager));
|
using var _ = new OnScopeExit(() => pageList.DecrementPagesReferenceCount(Context.MemoryManager));
|
||||||
|
|
||||||
@ -1276,7 +1276,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
KMemoryRegionManager region = GetMemoryRegionManager();
|
KMemoryRegionManager region = GetMemoryRegionManager();
|
||||||
|
|
||||||
KernelResult result = region.AllocatePages(remainingPages, _aslrDisabled, out KPageList pageList);
|
KernelResult result = region.AllocatePages(out KPageList pageList, remainingPages);
|
||||||
|
|
||||||
using var _ = new OnScopeExit(() => pageList.DecrementPagesReferenceCount(Context.MemoryManager));
|
using var _ = new OnScopeExit(() => pageList.DecrementPagesReferenceCount(Context.MemoryManager));
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
string GetReg(int x)
|
string GetReg(int x)
|
||||||
{
|
{
|
||||||
var v = x == 32 ? (ulong)thread.LastPc : context.GetX(x);
|
var v = x == 32 ? context.Pc : context.GetX(x);
|
||||||
if (!AnalyzePointer(out PointerInfo info, v, thread))
|
if (!AnalyzePointer(out PointerInfo info, v, thread))
|
||||||
{
|
{
|
||||||
return $"0x{v:x16}";
|
return $"0x{v:x16}";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using ARMeilleure.State;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -8,7 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
IVirtualMemoryManager AddressSpace { get; }
|
IVirtualMemoryManager AddressSpace { get; }
|
||||||
|
|
||||||
void Execute(ExecutionContext context, ulong codeAddress);
|
IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks);
|
||||||
|
void Execute(IExecutionContext context, ulong codeAddress);
|
||||||
void InvalidateCacheRegion(ulong address, ulong size);
|
void InvalidateCacheRegion(ulong address, ulong size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using ARMeilleure.State;
|
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
@ -736,22 +735,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
ulong argsPtr,
|
ulong argsPtr,
|
||||||
ulong stackTop,
|
ulong stackTop,
|
||||||
int priority,
|
int priority,
|
||||||
int cpuCore)
|
int cpuCore,
|
||||||
|
ThreadStart customThreadStart = null)
|
||||||
{
|
{
|
||||||
lock (_processLock)
|
lock (_processLock)
|
||||||
{
|
{
|
||||||
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null);
|
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, customThreadStart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SubscribeThreadEventHandlers(ARMeilleure.State.ExecutionContext context)
|
public IExecutionContext CreateExecutionContext()
|
||||||
{
|
{
|
||||||
context.Interrupt += InterruptHandler;
|
return Context?.CreateExecutionContext(new ExceptionCallbacks(
|
||||||
context.SupervisorCall += KernelContext.SyscallHandler.SvcCall;
|
InterruptHandler,
|
||||||
context.Undefined += UndefinedInstructionHandler;
|
null,
|
||||||
|
KernelContext.SyscallHandler.SvcCall,
|
||||||
|
UndefinedInstructionHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InterruptHandler(object sender, EventArgs e)
|
private void InterruptHandler(IExecutionContext context)
|
||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
|
|
||||||
@ -1093,12 +1095,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
|
private void UndefinedInstructionHandler(IExecutionContext context, ulong address, int opCode)
|
||||||
{
|
{
|
||||||
KernelStatic.GetCurrentThread().PrintGuestStackTrace();
|
KernelStatic.GetCurrentThread().PrintGuestStackTrace();
|
||||||
KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
|
KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
|
||||||
|
|
||||||
throw new UndefinedInstructionException(e.Address, e.OpCode);
|
throw new UndefinedInstructionException(address, opCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy() => Context.Dispose();
|
protected override void Destroy() => Context.Dispose();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using ARMeilleure.State;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -13,7 +13,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
AddressSpace = asManager;
|
AddressSpace = asManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ExecutionContext context, ulong codeAddress)
|
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||||
|
{
|
||||||
|
return new ProcessExecutionContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(IExecutionContext context, ulong codeAddress)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
44
Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs
Normal file
44
Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using ARMeilleure.State;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
{
|
||||||
|
class ProcessExecutionContext : IExecutionContext
|
||||||
|
{
|
||||||
|
public ulong Pc => 0UL;
|
||||||
|
|
||||||
|
public ulong CntfrqEl0 { get => 0; set { } }
|
||||||
|
public ulong CntpctEl0 => 0UL;
|
||||||
|
|
||||||
|
public long TpidrEl0 { get => 0; set { } }
|
||||||
|
public long TpidrroEl0 { get => 0; set { } }
|
||||||
|
|
||||||
|
public uint Pstate { get => 0; set { } }
|
||||||
|
|
||||||
|
public uint Fpcr { get => 0; set { } }
|
||||||
|
public uint Fpsr { get => 0; set { } }
|
||||||
|
|
||||||
|
public bool IsAarch32 { get => false; set { } }
|
||||||
|
|
||||||
|
public bool Running { get; private set; } = true;
|
||||||
|
|
||||||
|
public ulong GetX(int index) => 0UL;
|
||||||
|
public void SetX(int index, ulong value) { }
|
||||||
|
|
||||||
|
public V128 GetV(int index) => default;
|
||||||
|
public void SetV(int index, V128 value) { }
|
||||||
|
|
||||||
|
public void RequestInterrupt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopRunning()
|
||||||
|
{
|
||||||
|
Running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|
||||||
{
|
|
||||||
class InvalidSvcException : Exception
|
|
||||||
{
|
|
||||||
public InvalidSvcException(string message) : base(message) { }
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
class PointerSizedAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
|
||||||
public class RAttribute : Attribute
|
|
||||||
{
|
|
||||||
public readonly int Index;
|
|
||||||
|
|
||||||
public RAttribute(int index)
|
|
||||||
{
|
|
||||||
Index = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
15
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcAttribute.cs
Normal file
15
Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcAttribute.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
|
class SvcAttribute : Attribute
|
||||||
|
{
|
||||||
|
public int Id { get; }
|
||||||
|
|
||||||
|
public SvcAttribute(int id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||||
|
class SvcImplAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
{
|
{
|
||||||
|
[SvcImpl]
|
||||||
class Syscall
|
class Syscall
|
||||||
{
|
{
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
// Process
|
// Process
|
||||||
|
|
||||||
|
[Svc(0x24)]
|
||||||
public KernelResult GetProcessId(out ulong pid, int handle)
|
public KernelResult GetProcessId(out ulong pid, int handle)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
@ -167,9 +169,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x5f)]
|
||||||
|
public KernelResult FlushProcessDataCache(int processHandle, ulong address, ulong size)
|
||||||
|
{
|
||||||
|
// FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0.
|
||||||
|
// As we don't support (and don't actually need) to flush the cache, this is stubbed.
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
// IPC
|
// IPC
|
||||||
|
|
||||||
public KernelResult ConnectToNamedPort(out int handle, ulong namePtr)
|
[Svc(0x1f)]
|
||||||
|
public KernelResult ConnectToNamedPort(out int handle, [PointerSized] ulong namePtr)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
@ -222,6 +233,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x21)]
|
||||||
public KernelResult SendSyncRequest(int handle)
|
public KernelResult SendSyncRequest(int handle)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
@ -236,7 +248,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return session.SendSyncRequest();
|
return session.SendSyncRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle)
|
[Svc(0x22)]
|
||||||
|
public KernelResult SendSyncRequestWithUserBuffer(
|
||||||
|
[PointerSized] ulong messagePtr,
|
||||||
|
[PointerSized] ulong messageSize,
|
||||||
|
int handle)
|
||||||
{
|
{
|
||||||
if (!PageAligned(messagePtr))
|
if (!PageAligned(messagePtr))
|
||||||
{
|
{
|
||||||
@ -283,7 +299,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SendAsyncRequestWithUserBuffer(out int doneEventHandle, ulong messagePtr, ulong messageSize, int handle)
|
[Svc(0x23)]
|
||||||
|
public KernelResult SendAsyncRequestWithUserBuffer(
|
||||||
|
out int doneEventHandle,
|
||||||
|
[PointerSized] ulong messagePtr,
|
||||||
|
[PointerSized] ulong messageSize,
|
||||||
|
int handle)
|
||||||
{
|
{
|
||||||
doneEventHandle = 0;
|
doneEventHandle = 0;
|
||||||
|
|
||||||
@ -353,11 +374,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x40)]
|
||||||
public KernelResult CreateSession(
|
public KernelResult CreateSession(
|
||||||
out int serverSessionHandle,
|
out int serverSessionHandle,
|
||||||
out int clientSessionHandle,
|
out int clientSessionHandle,
|
||||||
bool isLight,
|
bool isLight,
|
||||||
ulong namePtr)
|
[PointerSized] ulong namePtr)
|
||||||
{
|
{
|
||||||
serverSessionHandle = 0;
|
serverSessionHandle = 0;
|
||||||
clientSessionHandle = 0;
|
clientSessionHandle = 0;
|
||||||
@ -419,6 +441,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x41)]
|
||||||
public KernelResult AcceptSession(out int sessionHandle, int portHandle)
|
public KernelResult AcceptSession(out int sessionHandle, int portHandle)
|
||||||
{
|
{
|
||||||
sessionHandle = 0;
|
sessionHandle = 0;
|
||||||
@ -470,9 +493,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x43)]
|
||||||
public KernelResult ReplyAndReceive(
|
public KernelResult ReplyAndReceive(
|
||||||
out int handleIndex,
|
out int handleIndex,
|
||||||
ulong handlesPtr,
|
[PointerSized] ulong handlesPtr,
|
||||||
int handlesCount,
|
int handlesCount,
|
||||||
int replyTargetHandle,
|
int replyTargetHandle,
|
||||||
long timeout)
|
long timeout)
|
||||||
@ -575,11 +599,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x44)]
|
||||||
public KernelResult ReplyAndReceiveWithUserBuffer(
|
public KernelResult ReplyAndReceiveWithUserBuffer(
|
||||||
out int handleIndex,
|
out int handleIndex,
|
||||||
ulong handlesPtr,
|
[PointerSized] ulong messagePtr,
|
||||||
ulong messagePtr,
|
[PointerSized] ulong messageSize,
|
||||||
ulong messageSize,
|
[PointerSized] ulong handlesPtr,
|
||||||
int handlesCount,
|
int handlesCount,
|
||||||
int replyTargetHandle,
|
int replyTargetHandle,
|
||||||
long timeout)
|
long timeout)
|
||||||
@ -679,12 +704,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x70)]
|
||||||
public KernelResult CreatePort(
|
public KernelResult CreatePort(
|
||||||
out int serverPortHandle,
|
out int serverPortHandle,
|
||||||
out int clientPortHandle,
|
out int clientPortHandle,
|
||||||
int maxSessions,
|
int maxSessions,
|
||||||
bool isLight,
|
bool isLight,
|
||||||
ulong namePtr)
|
[PointerSized] ulong namePtr)
|
||||||
{
|
{
|
||||||
serverPortHandle = clientPortHandle = 0;
|
serverPortHandle = clientPortHandle = 0;
|
||||||
|
|
||||||
@ -714,7 +740,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult ManageNamedPort(out int handle, ulong namePtr, int maxSessions)
|
[Svc(0x71)]
|
||||||
|
public KernelResult ManageNamedPort(out int handle, [PointerSized] ulong namePtr, int maxSessions)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
@ -766,6 +793,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x72)]
|
||||||
public KernelResult ConnectToPort(out int clientSessionHandle, int clientPortHandle)
|
public KernelResult ConnectToPort(out int clientSessionHandle, int clientPortHandle)
|
||||||
{
|
{
|
||||||
clientSessionHandle = 0;
|
clientSessionHandle = 0;
|
||||||
@ -819,7 +847,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
|
|
||||||
public KernelResult SetHeapSize(out ulong address, ulong size)
|
[Svc(1)]
|
||||||
|
public KernelResult SetHeapSize([PointerSized] out ulong address, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if ((size & 0xfffffffe001fffff) != 0)
|
if ((size & 0xfffffffe001fffff) != 0)
|
||||||
{
|
{
|
||||||
@ -833,7 +862,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.MemoryManager.SetHeapSize(size, out address);
|
return process.MemoryManager.SetHeapSize(size, out address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SetMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
|
[Svc(2)]
|
||||||
|
public KernelResult SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -865,9 +895,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission);
|
return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(3)]
|
||||||
public KernelResult SetMemoryAttribute(
|
public KernelResult SetMemoryAttribute(
|
||||||
ulong address,
|
[PointerSized] ulong address,
|
||||||
ulong size,
|
[PointerSized] ulong size,
|
||||||
MemoryAttribute attributeMask,
|
MemoryAttribute attributeMask,
|
||||||
MemoryAttribute attributeValue)
|
MemoryAttribute attributeValue)
|
||||||
{
|
{
|
||||||
@ -905,7 +936,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapMemory(ulong dst, ulong src, ulong size)
|
[Svc(4)]
|
||||||
|
public KernelResult MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(src | dst))
|
if (!PageAligned(src | dst))
|
||||||
{
|
{
|
||||||
@ -941,7 +973,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.MemoryManager.Map(dst, src, size);
|
return process.MemoryManager.Map(dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnmapMemory(ulong dst, ulong src, ulong size)
|
[Svc(5)]
|
||||||
|
public KernelResult UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(src | dst))
|
if (!PageAligned(src | dst))
|
||||||
{
|
{
|
||||||
@ -977,7 +1010,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.MemoryManager.Unmap(dst, src, size);
|
return process.MemoryManager.Unmap(dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult QueryMemory(ulong infoPtr, out ulong pageInfo, ulong address)
|
[Svc(6)]
|
||||||
|
public KernelResult QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address)
|
||||||
{
|
{
|
||||||
KernelResult result = QueryMemory(out MemoryInfo info, out pageInfo, address);
|
KernelResult result = QueryMemory(out MemoryInfo info, out pageInfo, address);
|
||||||
|
|
||||||
@ -1011,7 +1045,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapSharedMemory(int handle, ulong address, ulong size, KMemoryPermission permission)
|
[Svc(0x13)]
|
||||||
|
public KernelResult MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -1057,7 +1092,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
permission);
|
permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnmapSharedMemory(int handle, ulong address, ulong size)
|
[Svc(0x14)]
|
||||||
|
public KernelResult UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -1097,7 +1133,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
currentProcess);
|
currentProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult CreateTransferMemory(out int handle, ulong address, ulong size, KMemoryPermission permission)
|
[Svc(0x15)]
|
||||||
|
public KernelResult CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
@ -1160,7 +1197,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapTransferMemory(int handle, ulong address, ulong size, KMemoryPermission permission)
|
[Svc(0x51)]
|
||||||
|
public KernelResult MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -1206,7 +1244,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
permission);
|
permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnmapTransferMemory(int handle, ulong address, ulong size)
|
[Svc(0x52)]
|
||||||
|
public KernelResult UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -1246,7 +1285,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
currentProcess);
|
currentProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapPhysicalMemory(ulong address, ulong size)
|
[Svc(0x2c)]
|
||||||
|
public KernelResult MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -1281,7 +1321,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.MemoryManager.MapPhysicalMemory(address, size);
|
return process.MemoryManager.MapPhysicalMemory(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnmapPhysicalMemory(ulong address, ulong size)
|
[Svc(0x2d)]
|
||||||
|
public KernelResult UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(address))
|
if (!PageAligned(address))
|
||||||
{
|
{
|
||||||
@ -1316,7 +1357,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.MemoryManager.UnmapPhysicalMemory(address, size);
|
return process.MemoryManager.UnmapPhysicalMemory(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult CreateCodeMemory(ulong address, ulong size, out int handle)
|
[Svc(0x4b)]
|
||||||
|
public KernelResult CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
@ -1356,7 +1398,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return currentProcess.HandleTable.GenerateHandle(codeMemory, out handle);
|
return currentProcess.HandleTable.GenerateHandle(codeMemory, out handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult ControlCodeMemory(int handle, CodeMemoryOperation op, ulong address, ulong size, KMemoryPermission permission)
|
[Svc(0x4c)]
|
||||||
|
public KernelResult ControlCodeMemory(
|
||||||
|
int handle,
|
||||||
|
CodeMemoryOperation op,
|
||||||
|
ulong address,
|
||||||
|
ulong size,
|
||||||
|
KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
@ -1428,7 +1476,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission)
|
[Svc(0x73)]
|
||||||
|
public KernelResult SetProcessMemoryPermission(
|
||||||
|
int handle,
|
||||||
|
[PointerSized] ulong src,
|
||||||
|
[PointerSized] ulong size,
|
||||||
|
KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
if (!PageAligned(src))
|
if (!PageAligned(src))
|
||||||
{
|
{
|
||||||
@ -1465,7 +1518,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
|
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapProcessMemory(ulong dst, int handle, ulong src, ulong size)
|
[Svc(0x74)]
|
||||||
|
public KernelResult MapProcessMemory(
|
||||||
|
[PointerSized] ulong dst,
|
||||||
|
int handle,
|
||||||
|
ulong src,
|
||||||
|
[PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(src) || !PageAligned(dst))
|
if (!PageAligned(src) || !PageAligned(dst))
|
||||||
{
|
{
|
||||||
@ -1517,7 +1575,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite);
|
return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult UnmapProcessMemory(ulong dst, int handle, ulong src, ulong size)
|
[Svc(0x75)]
|
||||||
|
public KernelResult UnmapProcessMemory(
|
||||||
|
[PointerSized] ulong dst,
|
||||||
|
int handle,
|
||||||
|
ulong src,
|
||||||
|
[PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(src) || !PageAligned(dst))
|
if (!PageAligned(src) || !PageAligned(dst))
|
||||||
{
|
{
|
||||||
@ -1558,6 +1621,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x77)]
|
||||||
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(dst) || !PageAligned(src))
|
if (!PageAligned(dst) || !PageAligned(src))
|
||||||
@ -1595,6 +1659,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
|
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x78)]
|
||||||
public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||||
{
|
{
|
||||||
if (!PageAligned(dst) || !PageAligned(src))
|
if (!PageAligned(dst) || !PageAligned(src))
|
||||||
@ -1639,6 +1704,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
// System
|
// System
|
||||||
|
|
||||||
|
[Svc(0x7b)]
|
||||||
public KernelResult TerminateProcess(int handle)
|
public KernelResult TerminateProcess(int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -1668,11 +1734,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(7)]
|
||||||
public void ExitProcess()
|
public void ExitProcess()
|
||||||
{
|
{
|
||||||
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
|
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x11)]
|
||||||
public KernelResult SignalEvent(int handle)
|
public KernelResult SignalEvent(int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -1695,6 +1763,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x12)]
|
||||||
public KernelResult ClearEvent(int handle)
|
public KernelResult ClearEvent(int handle)
|
||||||
{
|
{
|
||||||
KernelResult result;
|
KernelResult result;
|
||||||
@ -1717,6 +1786,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x16)]
|
||||||
public KernelResult CloseHandle(int handle)
|
public KernelResult CloseHandle(int handle)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
@ -1724,6 +1794,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
|
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x17)]
|
||||||
public KernelResult ResetSignal(int handle)
|
public KernelResult ResetSignal(int handle)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
@ -1753,11 +1824,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x1e)]
|
||||||
public ulong GetSystemTick()
|
public ulong GetSystemTick()
|
||||||
{
|
{
|
||||||
return KernelStatic.GetCurrentThread().Context.CntpctEl0;
|
return _context.TickSource.Counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x26)]
|
||||||
public void Break(ulong reason)
|
public void Break(ulong reason)
|
||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
@ -1784,7 +1857,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OutputDebugString(ulong strPtr, ulong size)
|
[Svc(0x27)]
|
||||||
|
public void OutputDebugString([PointerSized] ulong strPtr, [PointerSized] ulong size)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
@ -1793,6 +1867,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
Logger.Warning?.Print(LogClass.KernelSvc, str);
|
Logger.Warning?.Print(LogClass.KernelSvc, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x29)]
|
||||||
public KernelResult GetInfo(out ulong value, InfoType id, int handle, long subId)
|
public KernelResult GetInfo(out ulong value, InfoType id, int handle, long subId)
|
||||||
{
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
@ -2038,6 +2113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x45)]
|
||||||
public KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
|
public KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
|
||||||
{
|
{
|
||||||
KEvent Event = new KEvent(_context);
|
KEvent Event = new KEvent(_context);
|
||||||
@ -2063,7 +2139,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult GetProcessList(out int count, ulong address, int maxCount)
|
[Svc(0x65)]
|
||||||
|
public KernelResult GetProcessList(out int count, [PointerSized] ulong address, int maxCount)
|
||||||
{
|
{
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
@ -2112,6 +2189,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x6f)]
|
||||||
public KernelResult GetSystemInfo(out long value, uint id, int handle, long subId)
|
public KernelResult GetSystemInfo(out long value, uint id, int handle, long subId)
|
||||||
{
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
@ -2168,6 +2246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x30)]
|
||||||
public KernelResult GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource)
|
public KernelResult GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource)
|
||||||
{
|
{
|
||||||
limitValue = 0;
|
limitValue = 0;
|
||||||
@ -2189,6 +2268,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x31)]
|
||||||
public KernelResult GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource)
|
public KernelResult GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource)
|
||||||
{
|
{
|
||||||
limitValue = 0;
|
limitValue = 0;
|
||||||
@ -2210,6 +2290,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x37)]
|
||||||
public KernelResult GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource)
|
public KernelResult GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource)
|
||||||
{
|
{
|
||||||
peak = 0;
|
peak = 0;
|
||||||
@ -2231,6 +2312,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x7d)]
|
||||||
public KernelResult CreateResourceLimit(out int handle)
|
public KernelResult CreateResourceLimit(out int handle)
|
||||||
{
|
{
|
||||||
KResourceLimit limit = new KResourceLimit(_context);
|
KResourceLimit limit = new KResourceLimit(_context);
|
||||||
@ -2240,6 +2322,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return process.HandleTable.GenerateHandle(limit, out handle);
|
return process.HandleTable.GenerateHandle(limit, out handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x7e)]
|
||||||
public KernelResult SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue)
|
public KernelResult SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue)
|
||||||
{
|
{
|
||||||
if (resource >= LimitableResource.Count)
|
if (resource >= LimitableResource.Count)
|
||||||
@ -2259,13 +2342,26 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
// Thread
|
// Thread
|
||||||
|
|
||||||
|
[Svc(8)]
|
||||||
|
public KernelResult CreateThread(
|
||||||
|
out int handle,
|
||||||
|
[PointerSized] ulong entrypoint,
|
||||||
|
[PointerSized] ulong argsPtr,
|
||||||
|
[PointerSized] ulong stackTop,
|
||||||
|
int priority,
|
||||||
|
int cpuCore)
|
||||||
|
{
|
||||||
|
return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null);
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult CreateThread(
|
public KernelResult CreateThread(
|
||||||
out int handle,
|
out int handle,
|
||||||
ulong entrypoint,
|
ulong entrypoint,
|
||||||
ulong argsPtr,
|
ulong argsPtr,
|
||||||
ulong stackTop,
|
ulong stackTop,
|
||||||
int priority,
|
int priority,
|
||||||
int cpuCore)
|
int cpuCore,
|
||||||
|
ThreadStart customThreadStart)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
@ -2302,7 +2398,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
argsPtr,
|
argsPtr,
|
||||||
stackTop,
|
stackTop,
|
||||||
priority,
|
priority,
|
||||||
cpuCore);
|
cpuCore,
|
||||||
|
customThreadStart);
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
@ -2320,6 +2417,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(9)]
|
||||||
public KernelResult StartThread(int handle)
|
public KernelResult StartThread(int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -2347,6 +2445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0xa)]
|
||||||
public void ExitThread()
|
public void ExitThread()
|
||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
@ -2354,6 +2453,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
currentThread.Exit();
|
currentThread.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0xb)]
|
||||||
public void SleepThread(long timeout)
|
public void SleepThread(long timeout)
|
||||||
{
|
{
|
||||||
if (timeout < 1)
|
if (timeout < 1)
|
||||||
@ -2371,6 +2471,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0xc)]
|
||||||
public KernelResult GetThreadPriority(out int priority, int handle)
|
public KernelResult GetThreadPriority(out int priority, int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -2391,6 +2492,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0xd)]
|
||||||
public KernelResult SetThreadPriority(int handle, int priority)
|
public KernelResult SetThreadPriority(int handle, int priority)
|
||||||
{
|
{
|
||||||
// TODO: NPDM check.
|
// TODO: NPDM check.
|
||||||
@ -2409,6 +2511,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0xe)]
|
||||||
public KernelResult GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle)
|
public KernelResult GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -2431,6 +2534,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0xf)]
|
||||||
public KernelResult SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask)
|
public KernelResult SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
@ -2479,11 +2583,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
|
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x10)]
|
||||||
public int GetCurrentProcessorNumber()
|
public int GetCurrentProcessorNumber()
|
||||||
{
|
{
|
||||||
return KernelStatic.GetCurrentThread().CurrentCore;
|
return KernelStatic.GetCurrentThread().CurrentCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x25)]
|
||||||
public KernelResult GetThreadId(out ulong threadUid, int handle)
|
public KernelResult GetThreadId(out ulong threadUid, int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -2504,6 +2610,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x32)]
|
||||||
public KernelResult SetThreadActivity(int handle, bool pause)
|
public KernelResult SetThreadActivity(int handle, bool pause)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -2528,7 +2635,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return thread.SetActivity(pause);
|
return thread.SetActivity(pause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult GetThreadContext3(ulong address, int handle)
|
[Svc(0x33)]
|
||||||
|
public KernelResult GetThreadContext3([PointerSized] ulong address, int handle)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
@ -2564,7 +2672,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
// Thread synchronization
|
// Thread synchronization
|
||||||
|
|
||||||
public KernelResult WaitSynchronization(out int handleIndex, ulong handlesPtr, int handlesCount, long timeout)
|
[Svc(0x18)]
|
||||||
|
public KernelResult WaitSynchronization(out int handleIndex, [PointerSized] ulong handlesPtr, int handlesCount, long timeout)
|
||||||
{
|
{
|
||||||
handleIndex = 0;
|
handleIndex = 0;
|
||||||
|
|
||||||
@ -2653,6 +2762,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x19)]
|
||||||
public KernelResult CancelSynchronization(int handle)
|
public KernelResult CancelSynchronization(int handle)
|
||||||
{
|
{
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
KProcess process = KernelStatic.GetCurrentProcess();
|
||||||
@ -2669,7 +2779,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
[Svc(0x1a)]
|
||||||
|
public KernelResult ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle)
|
||||||
{
|
{
|
||||||
if (IsPointingInsideKernel(mutexAddress))
|
if (IsPointingInsideKernel(mutexAddress))
|
||||||
{
|
{
|
||||||
@ -2686,7 +2797,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult ArbitrateUnlock(ulong mutexAddress)
|
[Svc(0x1b)]
|
||||||
|
public KernelResult ArbitrateUnlock([PointerSized] ulong mutexAddress)
|
||||||
{
|
{
|
||||||
if (IsPointingInsideKernel(mutexAddress))
|
if (IsPointingInsideKernel(mutexAddress))
|
||||||
{
|
{
|
||||||
@ -2703,9 +2815,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
|
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x1c)]
|
||||||
public KernelResult WaitProcessWideKeyAtomic(
|
public KernelResult WaitProcessWideKeyAtomic(
|
||||||
ulong mutexAddress,
|
[PointerSized] ulong mutexAddress,
|
||||||
ulong condVarAddress,
|
[PointerSized] ulong condVarAddress,
|
||||||
int handle,
|
int handle,
|
||||||
long timeout)
|
long timeout)
|
||||||
{
|
{
|
||||||
@ -2733,7 +2846,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
timeout);
|
timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SignalProcessWideKey(ulong address, int count)
|
[Svc(0x1d)]
|
||||||
|
public KernelResult SignalProcessWideKey([PointerSized] ulong address, int count)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
@ -2742,7 +2856,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult WaitForAddress(ulong address, ArbitrationType type, int value, long timeout)
|
[Svc(0x34)]
|
||||||
|
public KernelResult WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout)
|
||||||
{
|
{
|
||||||
if (IsPointingInsideKernel(address))
|
if (IsPointingInsideKernel(address))
|
||||||
{
|
{
|
||||||
@ -2773,7 +2888,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SignalToAddress(ulong address, SignalType type, int value, int count)
|
[Svc(0x35)]
|
||||||
|
public KernelResult SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count)
|
||||||
{
|
{
|
||||||
if (IsPointingInsideKernel(address))
|
if (IsPointingInsideKernel(address))
|
||||||
{
|
{
|
||||||
@ -2799,6 +2915,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Svc(0x36)]
|
||||||
public KernelResult SynchronizePreemptionState()
|
public KernelResult SynchronizePreemptionState()
|
||||||
{
|
{
|
||||||
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
|
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
|
||||||
@ -2806,12 +2923,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointingInsideKernel(ulong address)
|
private static bool IsPointingInsideKernel(ulong address)
|
||||||
{
|
{
|
||||||
return (address + 0x1000000000) < 0xffffff000;
|
return (address + 0x1000000000) < 0xffffff000;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsAddressNotWordAligned(ulong address)
|
private static bool IsAddressNotWordAligned(ulong address)
|
||||||
{
|
{
|
||||||
return (address & 3) != 0;
|
return (address & 3) != 0;
|
||||||
}
|
}
|
||||||
|
@ -1,534 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|
||||||
{
|
|
||||||
class Syscall32
|
|
||||||
{
|
|
||||||
private readonly Syscall _syscall;
|
|
||||||
|
|
||||||
public Syscall32(Syscall syscall)
|
|
||||||
{
|
|
||||||
_syscall = syscall;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPC
|
|
||||||
|
|
||||||
public KernelResult ConnectToNamedPort32([R(1)] uint namePtr, [R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ConnectToNamedPort(out handle, namePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequest32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.SendSyncRequest(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint messageSize, [R(2)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateSession32(
|
|
||||||
[R(2)] bool isLight,
|
|
||||||
[R(3)] uint namePtr,
|
|
||||||
[R(1)] out int serverSessionHandle,
|
|
||||||
[R(2)] out int clientSessionHandle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateSession(out serverSessionHandle, out clientSessionHandle, isLight, namePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult AcceptSession32([R(1)] int portHandle, [R(1)] out int sessionHandle)
|
|
||||||
{
|
|
||||||
return _syscall.AcceptSession(out sessionHandle, portHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ReplyAndReceive32(
|
|
||||||
[R(0)] uint timeoutLow,
|
|
||||||
[R(1)] uint handlesPtr,
|
|
||||||
[R(2)] int handlesCount,
|
|
||||||
[R(3)] int replyTargetHandle,
|
|
||||||
[R(4)] uint timeoutHigh,
|
|
||||||
[R(1)] out int handleIndex)
|
|
||||||
{
|
|
||||||
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
|
|
||||||
|
|
||||||
return _syscall.ReplyAndReceive(out handleIndex, handlesPtr, handlesCount, replyTargetHandle, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreatePort32(
|
|
||||||
[R(0)] uint namePtr,
|
|
||||||
[R(2)] int maxSessions,
|
|
||||||
[R(3)] bool isLight,
|
|
||||||
[R(1)] out int serverPortHandle,
|
|
||||||
[R(2)] out int clientPortHandle)
|
|
||||||
{
|
|
||||||
return _syscall.CreatePort(out serverPortHandle, out clientPortHandle, maxSessions, isLight, namePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ManageNamedPort32([R(1)] uint namePtr, [R(2)] int maxSessions, [R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ManageNamedPort(out handle, namePtr, maxSessions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ConnectToPort32([R(1)] int clientPortHandle, [R(1)] out int clientSessionHandle)
|
|
||||||
{
|
|
||||||
return _syscall.ConnectToPort(out clientSessionHandle, clientPortHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory
|
|
||||||
|
|
||||||
public KernelResult SetHeapSize32([R(1)] uint size, [R(1)] out uint address)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.SetHeapSize(out ulong address64, size);
|
|
||||||
|
|
||||||
address = (uint)address64;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetMemoryPermission32(
|
|
||||||
[R(0)] uint address,
|
|
||||||
[R(1)] uint size,
|
|
||||||
[R(2)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.SetMemoryPermission(address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetMemoryAttribute32(
|
|
||||||
[R(0)] uint address,
|
|
||||||
[R(1)] uint size,
|
|
||||||
[R(2)] MemoryAttribute attributeMask,
|
|
||||||
[R(3)] MemoryAttribute attributeValue)
|
|
||||||
{
|
|
||||||
return _syscall.SetMemoryAttribute(address, size, attributeMask, attributeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapMemory32([R(0)] uint dst, [R(1)] uint src, [R(2)] uint size)
|
|
||||||
{
|
|
||||||
return _syscall.MapMemory(dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapMemory32([R(0)] uint dst, [R(1)] uint src, [R(2)] uint size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapMemory(dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult QueryMemory32([R(0)] uint infoPtr, [R(1)] uint r1, [R(2)] uint address, [R(1)] out uint pageInfo)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.QueryMemory(infoPtr, out ulong pageInfo64, address);
|
|
||||||
|
|
||||||
pageInfo = (uint)pageInfo64;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.MapSharedMemory(handle, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapSharedMemory(handle, address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateTransferMemory32(
|
|
||||||
[R(1)] uint address,
|
|
||||||
[R(2)] uint size,
|
|
||||||
[R(3)] KMemoryPermission permission,
|
|
||||||
[R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateTransferMemory(out handle, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateCodeMemory32([R(1)] uint address, [R(2)] uint size, [R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateCodeMemory(address, size, out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ControlCodeMemory32(
|
|
||||||
[R(0)] int handle,
|
|
||||||
[R(1)] CodeMemoryOperation op,
|
|
||||||
[R(2)] uint addressLow,
|
|
||||||
[R(3)] uint addressHigh,
|
|
||||||
[R(4)] uint sizeLow,
|
|
||||||
[R(5)] uint sizeHigh,
|
|
||||||
[R(6)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
ulong address = addressLow | ((ulong)addressHigh << 32);
|
|
||||||
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.ControlCodeMemory(handle, op, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapTransferMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.MapTransferMemory(handle, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapTransferMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapTransferMemory(handle, address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapPhysicalMemory32([R(0)] uint address, [R(1)] uint size)
|
|
||||||
{
|
|
||||||
return _syscall.MapPhysicalMemory(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapPhysicalMemory32([R(0)] uint address, [R(1)] uint size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapPhysicalMemory(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission32(
|
|
||||||
[R(0)] int handle,
|
|
||||||
[R(1)] uint sizeLow,
|
|
||||||
[R(2)] uint srcLow,
|
|
||||||
[R(3)] uint srcHigh,
|
|
||||||
[R(4)] uint sizeHigh,
|
|
||||||
[R(5)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
|
||||||
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapProcessMemory32([R(0)] uint dst, [R(1)] int handle, [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint size)
|
|
||||||
{
|
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.MapProcessMemory(dst, handle, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapProcessMemory32([R(0)] uint dst, [R(1)] int handle, [R(2)] uint srcLow, [R(3)] uint srcHigh, [R(4)] uint size)
|
|
||||||
{
|
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.UnmapProcessMemory(dst, handle, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapProcessCodeMemory32([R(0)] int handle, [R(1)] uint srcLow, [R(2)] uint dstLow, [R(3)] uint dstHigh, [R(4)] uint srcHigh, [R(5)] uint sizeLow, [R(6)] uint sizeHigh)
|
|
||||||
{
|
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
|
||||||
ulong dst = dstLow | ((ulong)dstHigh << 32);
|
|
||||||
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.MapProcessCodeMemory(handle, dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapProcessCodeMemory32([R(0)] int handle, [R(1)] uint srcLow, [R(2)] uint dstLow, [R(3)] uint dstHigh, [R(4)] uint srcHigh, [R(5)] uint sizeLow, [R(6)] uint sizeHigh)
|
|
||||||
{
|
|
||||||
ulong src = srcLow | ((ulong)srcHigh << 32);
|
|
||||||
ulong dst = dstLow | ((ulong)dstHigh << 32);
|
|
||||||
ulong size = sizeLow | ((ulong)sizeHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// System
|
|
||||||
|
|
||||||
public void ExitProcess32()
|
|
||||||
{
|
|
||||||
_syscall.ExitProcess();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult TerminateProcess32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.TerminateProcess(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SignalEvent32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.SignalEvent(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ClearEvent32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ClearEvent(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CloseHandle32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CloseHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ResetSignal32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ResetSignal(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetSystemTick32([R(0)] out uint resultLow, [R(1)] out uint resultHigh)
|
|
||||||
{
|
|
||||||
ulong result = _syscall.GetSystemTick();
|
|
||||||
|
|
||||||
resultLow = (uint)(result & uint.MaxValue);
|
|
||||||
resultHigh = (uint)(result >> 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetProcessId32([R(1)] int handle, [R(1)] out uint pidLow, [R(2)] out uint pidHigh)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.GetProcessId(out ulong pid, handle);
|
|
||||||
|
|
||||||
pidLow = (uint)(pid & uint.MaxValue);
|
|
||||||
pidHigh = (uint)(pid >> 32);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Break32([R(0)] uint reason, [R(1)] uint r1, [R(2)] uint info)
|
|
||||||
{
|
|
||||||
_syscall.Break(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OutputDebugString32([R(0)] uint strPtr, [R(1)] uint size)
|
|
||||||
{
|
|
||||||
_syscall.OutputDebugString(strPtr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetInfo32(
|
|
||||||
[R(0)] uint subIdLow,
|
|
||||||
[R(1)] InfoType id,
|
|
||||||
[R(2)] int handle,
|
|
||||||
[R(3)] uint subIdHigh,
|
|
||||||
[R(1)] out uint valueLow,
|
|
||||||
[R(2)] out uint valueHigh)
|
|
||||||
{
|
|
||||||
long subId = (long)(subIdLow | ((ulong)subIdHigh << 32));
|
|
||||||
|
|
||||||
KernelResult result = _syscall.GetInfo(out ulong value, id, handle, subId);
|
|
||||||
|
|
||||||
valueHigh = (uint)(value >> 32);
|
|
||||||
valueLow = (uint)(value & uint.MaxValue);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateEvent32([R(1)] out int wEventHandle, [R(2)] out int rEventHandle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateEvent(out wEventHandle, out rEventHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetProcessList32([R(1)] ulong address, [R(2)] int maxCount, [R(1)] out int count)
|
|
||||||
{
|
|
||||||
return _syscall.GetProcessList(out count, address, maxCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetSystemInfo32([R(1)] uint subIdLow, [R(2)] uint id, [R(3)] int handle, [R(3)] uint subIdHigh, [R(1)] out int valueLow, [R(2)] out int valueHigh)
|
|
||||||
{
|
|
||||||
long subId = (long)(subIdLow | ((ulong)subIdHigh << 32));
|
|
||||||
|
|
||||||
KernelResult result = _syscall.GetSystemInfo(out long value, id, handle, subId);
|
|
||||||
|
|
||||||
valueHigh = (int)(value >> 32);
|
|
||||||
valueLow = (int)(value & uint.MaxValue);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetResourceLimitLimitValue32([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out int limitValueLow, [R(2)] out int limitValueHigh)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.GetResourceLimitLimitValue(out long limitValue, handle, resource);
|
|
||||||
|
|
||||||
limitValueHigh = (int)(limitValue >> 32);
|
|
||||||
limitValueLow = (int)(limitValue & uint.MaxValue);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetResourceLimitCurrentValue32([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out int limitValueLow, [R(2)] out int limitValueHigh)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.GetResourceLimitCurrentValue(out long limitValue, handle, resource);
|
|
||||||
|
|
||||||
limitValueHigh = (int)(limitValue >> 32);
|
|
||||||
limitValueLow = (int)(limitValue & uint.MaxValue);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetResourceLimitPeakValue32([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out int peakLow, [R(2)] out int peakHigh)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.GetResourceLimitPeakValue(out long peak, handle, resource);
|
|
||||||
|
|
||||||
peakHigh = (int)(peak >> 32);
|
|
||||||
peakLow = (int)(peak & uint.MaxValue);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateResourceLimit32([R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateResourceLimit(out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetResourceLimitLimitValue32([R(0)] int handle, [R(1)] LimitableResource resource, [R(2)] uint limitValueLow, [R(3)] uint limitValueHigh)
|
|
||||||
{
|
|
||||||
long limitValue = (long)(limitValueLow | ((ulong)limitValueHigh << 32));
|
|
||||||
|
|
||||||
return _syscall.SetResourceLimitLimitValue(handle, resource, limitValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult FlushProcessDataCache32(
|
|
||||||
[R(0)] uint processHandle,
|
|
||||||
[R(2)] uint addressLow,
|
|
||||||
[R(3)] uint addressHigh,
|
|
||||||
[R(1)] uint sizeLow,
|
|
||||||
[R(4)] uint sizeHigh)
|
|
||||||
{
|
|
||||||
// FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0.
|
|
||||||
// As we don't support (and don't actually need) to flush the cache, this is stubbed.
|
|
||||||
return KernelResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread
|
|
||||||
|
|
||||||
public KernelResult CreateThread32(
|
|
||||||
[R(1)] uint entrypoint,
|
|
||||||
[R(2)] uint argsPtr,
|
|
||||||
[R(3)] uint stackTop,
|
|
||||||
[R(0)] int priority,
|
|
||||||
[R(4)] int cpuCore,
|
|
||||||
[R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult StartThread32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.StartThread(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExitThread32()
|
|
||||||
{
|
|
||||||
_syscall.ExitThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SleepThread32([R(0)] uint timeoutLow, [R(1)] uint timeoutHigh)
|
|
||||||
{
|
|
||||||
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
|
|
||||||
|
|
||||||
_syscall.SleepThread(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadPriority32([R(1)] int handle, [R(1)] out int priority)
|
|
||||||
{
|
|
||||||
return _syscall.GetThreadPriority(out priority, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetThreadPriority32([R(0)] int handle, [R(1)] int priority)
|
|
||||||
{
|
|
||||||
return _syscall.SetThreadPriority(handle, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadCoreMask32([R(2)] int handle, [R(1)] out int preferredCore, [R(2)] out uint affinityMaskLow, [R(3)] out uint affinityMaskHigh)
|
|
||||||
{
|
|
||||||
KernelResult result = _syscall.GetThreadCoreMask(out preferredCore, out ulong affinityMask, handle);
|
|
||||||
|
|
||||||
affinityMaskLow = (uint)(affinityMask & uint.MaxValue);
|
|
||||||
affinityMaskHigh = (uint)(affinityMask >> 32);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetThreadCoreMask32([R(0)] int handle, [R(1)] int preferredCore, [R(2)] uint affinityMaskLow, [R(3)] uint affinityMaskHigh)
|
|
||||||
{
|
|
||||||
ulong affinityMask = affinityMaskLow | ((ulong)affinityMaskHigh << 32);
|
|
||||||
|
|
||||||
return _syscall.SetThreadCoreMask(handle, preferredCore, affinityMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCurrentProcessorNumber32()
|
|
||||||
{
|
|
||||||
return _syscall.GetCurrentProcessorNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadId32([R(1)] int handle, [R(1)] out uint threadUidLow, [R(2)] out uint threadUidHigh)
|
|
||||||
{
|
|
||||||
ulong threadUid;
|
|
||||||
|
|
||||||
KernelResult result = _syscall.GetThreadId(out threadUid, handle);
|
|
||||||
|
|
||||||
threadUidLow = (uint)(threadUid >> 32);
|
|
||||||
threadUidHigh = (uint)(threadUid & uint.MaxValue);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetThreadActivity32([R(0)] int handle, [R(1)] bool pause)
|
|
||||||
{
|
|
||||||
return _syscall.SetThreadActivity(handle, pause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadContext332([R(0)] uint address, [R(1)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.GetThreadContext3(address, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread synchronization
|
|
||||||
|
|
||||||
public KernelResult WaitSynchronization32(
|
|
||||||
[R(0)] uint timeoutLow,
|
|
||||||
[R(1)] uint handlesPtr,
|
|
||||||
[R(2)] int handlesCount,
|
|
||||||
[R(3)] uint timeoutHigh,
|
|
||||||
[R(1)] out int handleIndex)
|
|
||||||
{
|
|
||||||
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
|
|
||||||
|
|
||||||
return _syscall.WaitSynchronization(out handleIndex, handlesPtr, handlesCount, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CancelSynchronization32([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CancelSynchronization(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public KernelResult ArbitrateLock32([R(0)] int ownerHandle, [R(1)] uint mutexAddress, [R(2)] int requesterHandle)
|
|
||||||
{
|
|
||||||
return _syscall.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ArbitrateUnlock32([R(0)] uint mutexAddress)
|
|
||||||
{
|
|
||||||
return _syscall.ArbitrateUnlock(mutexAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult WaitProcessWideKeyAtomic32(
|
|
||||||
[R(0)] uint mutexAddress,
|
|
||||||
[R(1)] uint condVarAddress,
|
|
||||||
[R(2)] int handle,
|
|
||||||
[R(3)] uint timeoutLow,
|
|
||||||
[R(4)] uint timeoutHigh)
|
|
||||||
{
|
|
||||||
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
|
|
||||||
|
|
||||||
return _syscall.WaitProcessWideKeyAtomic(mutexAddress, condVarAddress, handle, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SignalProcessWideKey32([R(0)] uint address, [R(1)] int count)
|
|
||||||
{
|
|
||||||
return _syscall.SignalProcessWideKey(address, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult WaitForAddress32([R(0)] uint address, [R(1)] ArbitrationType type, [R(2)] int value, [R(3)] uint timeoutLow, [R(4)] uint timeoutHigh)
|
|
||||||
{
|
|
||||||
long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32));
|
|
||||||
|
|
||||||
return _syscall.WaitForAddress(address, type, value, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SignalToAddress32([R(0)] uint address, [R(1)] SignalType type, [R(2)] int value, [R(3)] int count)
|
|
||||||
{
|
|
||||||
return _syscall.SignalToAddress(address, type, value, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SynchronizePreemptionState32()
|
|
||||||
{
|
|
||||||
return _syscall.SynchronizePreemptionState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,434 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|
||||||
{
|
|
||||||
class Syscall64
|
|
||||||
{
|
|
||||||
private readonly Syscall _syscall;
|
|
||||||
|
|
||||||
public Syscall64(Syscall syscall)
|
|
||||||
{
|
|
||||||
_syscall = syscall;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPC
|
|
||||||
|
|
||||||
public KernelResult ConnectToNamedPort64([R(1)] ulong namePtr, [R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ConnectToNamedPort(out handle, namePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequest64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.SendSyncRequest(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong messageSize, [R(2)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendAsyncRequestWithUserBuffer64(
|
|
||||||
[R(1)] ulong messagePtr,
|
|
||||||
[R(2)] ulong messageSize,
|
|
||||||
[R(3)] int handle,
|
|
||||||
[R(1)] out int doneEventHandle)
|
|
||||||
{
|
|
||||||
return _syscall.SendAsyncRequestWithUserBuffer(out doneEventHandle, messagePtr, messageSize, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateSession64(
|
|
||||||
[R(2)] bool isLight,
|
|
||||||
[R(3)] ulong namePtr,
|
|
||||||
[R(1)] out int serverSessionHandle,
|
|
||||||
[R(2)] out int clientSessionHandle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateSession(out serverSessionHandle, out clientSessionHandle, isLight, namePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult AcceptSession64([R(1)] int portHandle, [R(1)] out int sessionHandle)
|
|
||||||
{
|
|
||||||
return _syscall.AcceptSession(out sessionHandle, portHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ReplyAndReceive64(
|
|
||||||
[R(1)] ulong handlesPtr,
|
|
||||||
[R(2)] int handlesCount,
|
|
||||||
[R(3)] int replyTargetHandle,
|
|
||||||
[R(4)] long timeout,
|
|
||||||
[R(1)] out int handleIndex)
|
|
||||||
{
|
|
||||||
return _syscall.ReplyAndReceive(out handleIndex, handlesPtr, handlesCount, replyTargetHandle, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ReplyAndReceiveWithUserBuffer64(
|
|
||||||
[R(1)] ulong messagePtr,
|
|
||||||
[R(2)] ulong messageSize,
|
|
||||||
[R(3)] ulong handlesPtr,
|
|
||||||
[R(4)] int handlesCount,
|
|
||||||
[R(5)] int replyTargetHandle,
|
|
||||||
[R(6)] long timeout,
|
|
||||||
[R(1)] out int handleIndex)
|
|
||||||
{
|
|
||||||
return _syscall.ReplyAndReceiveWithUserBuffer(
|
|
||||||
out handleIndex,
|
|
||||||
handlesPtr,
|
|
||||||
messagePtr,
|
|
||||||
messageSize,
|
|
||||||
handlesCount,
|
|
||||||
replyTargetHandle,
|
|
||||||
timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreatePort64(
|
|
||||||
[R(2)] int maxSessions,
|
|
||||||
[R(3)] bool isLight,
|
|
||||||
[R(4)] ulong namePtr,
|
|
||||||
[R(1)] out int serverPortHandle,
|
|
||||||
[R(2)] out int clientPortHandle)
|
|
||||||
{
|
|
||||||
return _syscall.CreatePort(out serverPortHandle, out clientPortHandle, maxSessions, isLight, namePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ManageNamedPort64([R(1)] ulong namePtr, [R(2)] int maxSessions, [R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ManageNamedPort(out handle, namePtr, maxSessions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ConnectToPort64([R(1)] int clientPortHandle, [R(1)] out int clientSessionHandle)
|
|
||||||
{
|
|
||||||
return _syscall.ConnectToPort(out clientSessionHandle, clientPortHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory
|
|
||||||
|
|
||||||
public KernelResult SetHeapSize64([R(1)] ulong size, [R(1)] out ulong address)
|
|
||||||
{
|
|
||||||
return _syscall.SetHeapSize(out address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetMemoryPermission64(
|
|
||||||
[R(0)] ulong address,
|
|
||||||
[R(1)] ulong size,
|
|
||||||
[R(2)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.SetMemoryPermission(address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetMemoryAttribute64(
|
|
||||||
[R(0)] ulong address,
|
|
||||||
[R(1)] ulong size,
|
|
||||||
[R(2)] MemoryAttribute attributeMask,
|
|
||||||
[R(3)] MemoryAttribute attributeValue)
|
|
||||||
{
|
|
||||||
return _syscall.SetMemoryAttribute(address, size, attributeMask, attributeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapMemory64([R(0)] ulong dst, [R(1)] ulong src, [R(2)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.MapMemory(dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapMemory64([R(0)] ulong dst, [R(1)] ulong src, [R(2)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapMemory(dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult QueryMemory64([R(0)] ulong infoPtr, [R(2)] ulong address, [R(1)] out ulong pageInfo)
|
|
||||||
{
|
|
||||||
return _syscall.QueryMemory(infoPtr, out pageInfo, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.MapSharedMemory(handle, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapSharedMemory(handle, address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateTransferMemory64(
|
|
||||||
[R(1)] ulong address,
|
|
||||||
[R(2)] ulong size,
|
|
||||||
[R(3)] KMemoryPermission permission,
|
|
||||||
[R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateTransferMemory(out handle, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateCodeMemory64([R(1)] ulong address, [R(2)] ulong size, [R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateCodeMemory(address, size, out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ControlCodeMemory64([R(0)] int handle, [R(1)] CodeMemoryOperation op, [R(2)] ulong address, [R(3)] ulong size, [R(4)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.ControlCodeMemory(handle, op, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.MapTransferMemory(handle, address, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapTransferMemory(handle, address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapPhysicalMemory64([R(0)] ulong address, [R(1)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.MapPhysicalMemory(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapPhysicalMemory64([R(0)] ulong address, [R(1)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapPhysicalMemory(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
|
|
||||||
{
|
|
||||||
return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.MapProcessMemory(dst, handle, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapProcessMemory(dst, handle, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult MapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.MapProcessCodeMemory(handle, dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult UnmapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size)
|
|
||||||
{
|
|
||||||
return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// System
|
|
||||||
|
|
||||||
public void ExitProcess64()
|
|
||||||
{
|
|
||||||
_syscall.ExitProcess();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult TerminateProcess64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.TerminateProcess(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SignalEvent64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.SignalEvent(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ClearEvent64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ClearEvent(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CloseHandle64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CloseHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ResetSignal64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.ResetSignal(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong GetSystemTick64()
|
|
||||||
{
|
|
||||||
return _syscall.GetSystemTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetProcessId64([R(1)] int handle, [R(1)] out ulong pid)
|
|
||||||
{
|
|
||||||
return _syscall.GetProcessId(out pid, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Break64([R(0)] ulong reason, [R(1)] ulong x1, [R(2)] ulong info)
|
|
||||||
{
|
|
||||||
_syscall.Break(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OutputDebugString64([R(0)] ulong strPtr, [R(1)] ulong size)
|
|
||||||
{
|
|
||||||
_syscall.OutputDebugString(strPtr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetInfo64([R(1)] InfoType id, [R(2)] int handle, [R(3)] long subId, [R(1)] out ulong value)
|
|
||||||
{
|
|
||||||
return _syscall.GetInfo(out value, id, handle, subId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateEvent64([R(1)] out int wEventHandle, [R(2)] out int rEventHandle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateEvent(out wEventHandle, out rEventHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetProcessList64([R(1)] ulong address, [R(2)] int maxCount, [R(1)] out int count)
|
|
||||||
{
|
|
||||||
return _syscall.GetProcessList(out count, address, maxCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetSystemInfo64([R(1)] uint id, [R(2)] int handle, [R(3)] long subId, [R(1)] out long value)
|
|
||||||
{
|
|
||||||
return _syscall.GetSystemInfo(out value, id, handle, subId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetResourceLimitLimitValue64([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out long limitValue)
|
|
||||||
{
|
|
||||||
return _syscall.GetResourceLimitLimitValue(out limitValue, handle, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetResourceLimitCurrentValue64([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out long limitValue)
|
|
||||||
{
|
|
||||||
return _syscall.GetResourceLimitCurrentValue(out limitValue, handle, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetResourceLimitPeakValue64([R(1)] int handle, [R(2)] LimitableResource resource, [R(1)] out long peak)
|
|
||||||
{
|
|
||||||
return _syscall.GetResourceLimitPeakValue(out peak, handle, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CreateResourceLimit64([R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateResourceLimit(out handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetResourceLimitLimitValue64([R(0)] int handle, [R(1)] LimitableResource resource, [R(2)] long limitValue)
|
|
||||||
{
|
|
||||||
return _syscall.SetResourceLimitLimitValue(handle, resource, limitValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread
|
|
||||||
|
|
||||||
public KernelResult CreateThread64(
|
|
||||||
[R(1)] ulong entrypoint,
|
|
||||||
[R(2)] ulong argsPtr,
|
|
||||||
[R(3)] ulong stackTop,
|
|
||||||
[R(4)] int priority,
|
|
||||||
[R(5)] int cpuCore,
|
|
||||||
[R(1)] out int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult StartThread64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.StartThread(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExitThread64()
|
|
||||||
{
|
|
||||||
_syscall.ExitThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SleepThread64([R(0)] long timeout)
|
|
||||||
{
|
|
||||||
_syscall.SleepThread(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadPriority64([R(1)] int handle, [R(1)] out int priority)
|
|
||||||
{
|
|
||||||
return _syscall.GetThreadPriority(out priority, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetThreadPriority64([R(0)] int handle, [R(1)] int priority)
|
|
||||||
{
|
|
||||||
return _syscall.SetThreadPriority(handle, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadCoreMask64([R(2)] int handle, [R(1)] out int preferredCore, [R(2)] out ulong affinityMask)
|
|
||||||
{
|
|
||||||
return _syscall.GetThreadCoreMask(out preferredCore, out affinityMask, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetThreadCoreMask64([R(0)] int handle, [R(1)] int preferredCore, [R(2)] ulong affinityMask)
|
|
||||||
{
|
|
||||||
return _syscall.SetThreadCoreMask(handle, preferredCore, affinityMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCurrentProcessorNumber64()
|
|
||||||
{
|
|
||||||
return _syscall.GetCurrentProcessorNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadId64([R(1)] int handle, [R(1)] out ulong threadUid)
|
|
||||||
{
|
|
||||||
return _syscall.GetThreadId(out threadUid, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SetThreadActivity64([R(0)] int handle, [R(1)] bool pause)
|
|
||||||
{
|
|
||||||
return _syscall.SetThreadActivity(handle, pause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult GetThreadContext364([R(0)] ulong address, [R(1)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.GetThreadContext3(address, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread synchronization
|
|
||||||
|
|
||||||
public KernelResult WaitSynchronization64([R(1)] ulong handlesPtr, [R(2)] int handlesCount, [R(3)] long timeout, [R(1)] out int handleIndex)
|
|
||||||
{
|
|
||||||
return _syscall.WaitSynchronization(out handleIndex, handlesPtr, handlesCount, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult CancelSynchronization64([R(0)] int handle)
|
|
||||||
{
|
|
||||||
return _syscall.CancelSynchronization(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ArbitrateLock64([R(0)] int ownerHandle, [R(1)] ulong mutexAddress, [R(2)] int requesterHandle)
|
|
||||||
{
|
|
||||||
return _syscall.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult ArbitrateUnlock64([R(0)] ulong mutexAddress)
|
|
||||||
{
|
|
||||||
return _syscall.ArbitrateUnlock(mutexAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult WaitProcessWideKeyAtomic64(
|
|
||||||
[R(0)] ulong mutexAddress,
|
|
||||||
[R(1)] ulong condVarAddress,
|
|
||||||
[R(2)] int handle,
|
|
||||||
[R(3)] long timeout)
|
|
||||||
{
|
|
||||||
return _syscall.WaitProcessWideKeyAtomic(mutexAddress, condVarAddress, handle, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SignalProcessWideKey64([R(0)] ulong address, [R(1)] int count)
|
|
||||||
{
|
|
||||||
return _syscall.SignalProcessWideKey(address, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult WaitForAddress64([R(0)] ulong address, [R(1)] ArbitrationType type, [R(2)] int value, [R(3)] long timeout)
|
|
||||||
{
|
|
||||||
return _syscall.WaitForAddress(address, type, value, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SignalToAddress64([R(0)] ulong address, [R(1)] SignalType type, [R(2)] int value, [R(3)] int count)
|
|
||||||
{
|
|
||||||
return _syscall.SignalToAddress(address, type, value, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SynchronizePreemptionState64()
|
|
||||||
{
|
|
||||||
return _syscall.SynchronizePreemptionState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +1,18 @@
|
|||||||
using ARMeilleure.State;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
{
|
{
|
||||||
partial class SyscallHandler
|
partial class SyscallHandler
|
||||||
{
|
{
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private readonly Syscall32 _syscall32;
|
|
||||||
private readonly Syscall64 _syscall64;
|
|
||||||
|
|
||||||
public SyscallHandler(KernelContext context)
|
public SyscallHandler(KernelContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_syscall32 = new Syscall32(context.Syscall);
|
|
||||||
_syscall64 = new Syscall64(context.Syscall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SvcCall(object sender, InstExceptionEventArgs e)
|
public void SvcCall(IExecutionContext context, ulong address, int id)
|
||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
|
|
||||||
@ -34,29 +29,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
_context.CriticalSection.Leave();
|
_context.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutionContext context = (ExecutionContext)sender;
|
|
||||||
|
|
||||||
if (context.IsAarch32)
|
if (context.IsAarch32)
|
||||||
{
|
{
|
||||||
var svcFunc = SyscallTable.SvcTable32[e.Id];
|
SyscallDispatch.Dispatch32(_context.Syscall, context, id);
|
||||||
|
|
||||||
if (svcFunc == null)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
svcFunc(_syscall32, context);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var svcFunc = SyscallTable.SvcTable64[e.Id];
|
SyscallDispatch.Dispatch64(_context.Syscall, context, id);
|
||||||
|
|
||||||
if (svcFunc == null)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
svcFunc(_syscall64, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.HandlePostSyscall();
|
currentThread.HandlePostSyscall();
|
||||||
|
@ -1,494 +0,0 @@
|
|||||||
using ARMeilleure.State;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|
||||||
{
|
|
||||||
static class SyscallTable
|
|
||||||
{
|
|
||||||
private const int SvcFuncMaxArguments64 = 8;
|
|
||||||
private const int SvcFuncMaxArguments32 = 4;
|
|
||||||
private const int SvcMax = 0x80;
|
|
||||||
|
|
||||||
public static Action<Syscall32, ExecutionContext>[] SvcTable32 { get; }
|
|
||||||
public static Action<Syscall64, ExecutionContext>[] SvcTable64 { get; }
|
|
||||||
|
|
||||||
static SyscallTable()
|
|
||||||
{
|
|
||||||
SvcTable32 = new Action<Syscall32, ExecutionContext>[SvcMax];
|
|
||||||
SvcTable64 = new Action<Syscall64, ExecutionContext>[SvcMax];
|
|
||||||
|
|
||||||
Dictionary<int, string> svcFuncs64 = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
{ 0x01, nameof(Syscall64.SetHeapSize64) },
|
|
||||||
{ 0x02, nameof(Syscall64.SetMemoryPermission64) },
|
|
||||||
{ 0x03, nameof(Syscall64.SetMemoryAttribute64) },
|
|
||||||
{ 0x04, nameof(Syscall64.MapMemory64) },
|
|
||||||
{ 0x05, nameof(Syscall64.UnmapMemory64) },
|
|
||||||
{ 0x06, nameof(Syscall64.QueryMemory64) },
|
|
||||||
{ 0x07, nameof(Syscall64.ExitProcess64) },
|
|
||||||
{ 0x08, nameof(Syscall64.CreateThread64) },
|
|
||||||
{ 0x09, nameof(Syscall64.StartThread64) },
|
|
||||||
{ 0x0a, nameof(Syscall64.ExitThread64) },
|
|
||||||
{ 0x0b, nameof(Syscall64.SleepThread64) },
|
|
||||||
{ 0x0c, nameof(Syscall64.GetThreadPriority64) },
|
|
||||||
{ 0x0d, nameof(Syscall64.SetThreadPriority64) },
|
|
||||||
{ 0x0e, nameof(Syscall64.GetThreadCoreMask64) },
|
|
||||||
{ 0x0f, nameof(Syscall64.SetThreadCoreMask64) },
|
|
||||||
{ 0x10, nameof(Syscall64.GetCurrentProcessorNumber64) },
|
|
||||||
{ 0x11, nameof(Syscall64.SignalEvent64) },
|
|
||||||
{ 0x12, nameof(Syscall64.ClearEvent64) },
|
|
||||||
{ 0x13, nameof(Syscall64.MapSharedMemory64) },
|
|
||||||
{ 0x14, nameof(Syscall64.UnmapSharedMemory64) },
|
|
||||||
{ 0x15, nameof(Syscall64.CreateTransferMemory64) },
|
|
||||||
{ 0x16, nameof(Syscall64.CloseHandle64) },
|
|
||||||
{ 0x17, nameof(Syscall64.ResetSignal64) },
|
|
||||||
{ 0x18, nameof(Syscall64.WaitSynchronization64) },
|
|
||||||
{ 0x19, nameof(Syscall64.CancelSynchronization64) },
|
|
||||||
{ 0x1a, nameof(Syscall64.ArbitrateLock64) },
|
|
||||||
{ 0x1b, nameof(Syscall64.ArbitrateUnlock64) },
|
|
||||||
{ 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64) },
|
|
||||||
{ 0x1d, nameof(Syscall64.SignalProcessWideKey64) },
|
|
||||||
{ 0x1e, nameof(Syscall64.GetSystemTick64) },
|
|
||||||
{ 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
|
|
||||||
{ 0x21, nameof(Syscall64.SendSyncRequest64) },
|
|
||||||
{ 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
|
|
||||||
{ 0x23, nameof(Syscall64.SendAsyncRequestWithUserBuffer64) },
|
|
||||||
{ 0x24, nameof(Syscall64.GetProcessId64) },
|
|
||||||
{ 0x25, nameof(Syscall64.GetThreadId64) },
|
|
||||||
{ 0x26, nameof(Syscall64.Break64) },
|
|
||||||
{ 0x27, nameof(Syscall64.OutputDebugString64) },
|
|
||||||
{ 0x29, nameof(Syscall64.GetInfo64) },
|
|
||||||
{ 0x2c, nameof(Syscall64.MapPhysicalMemory64) },
|
|
||||||
{ 0x2d, nameof(Syscall64.UnmapPhysicalMemory64) },
|
|
||||||
{ 0x30, nameof(Syscall64.GetResourceLimitLimitValue64) },
|
|
||||||
{ 0x31, nameof(Syscall64.GetResourceLimitCurrentValue64) },
|
|
||||||
{ 0x32, nameof(Syscall64.SetThreadActivity64) },
|
|
||||||
{ 0x33, nameof(Syscall64.GetThreadContext364) },
|
|
||||||
{ 0x34, nameof(Syscall64.WaitForAddress64) },
|
|
||||||
{ 0x35, nameof(Syscall64.SignalToAddress64) },
|
|
||||||
{ 0x36, nameof(Syscall64.SynchronizePreemptionState64) },
|
|
||||||
{ 0x37, nameof(Syscall64.GetResourceLimitPeakValue64) },
|
|
||||||
{ 0x40, nameof(Syscall64.CreateSession64) },
|
|
||||||
{ 0x41, nameof(Syscall64.AcceptSession64) },
|
|
||||||
{ 0x43, nameof(Syscall64.ReplyAndReceive64) },
|
|
||||||
{ 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
|
|
||||||
{ 0x45, nameof(Syscall64.CreateEvent64) },
|
|
||||||
{ 0x4b, nameof(Syscall64.CreateCodeMemory64) },
|
|
||||||
{ 0x4c, nameof(Syscall64.ControlCodeMemory64) },
|
|
||||||
{ 0x51, nameof(Syscall64.MapTransferMemory64) },
|
|
||||||
{ 0x52, nameof(Syscall64.UnmapTransferMemory64) },
|
|
||||||
{ 0x65, nameof(Syscall64.GetProcessList64) },
|
|
||||||
{ 0x6f, nameof(Syscall64.GetSystemInfo64) },
|
|
||||||
{ 0x70, nameof(Syscall64.CreatePort64) },
|
|
||||||
{ 0x71, nameof(Syscall64.ManageNamedPort64) },
|
|
||||||
{ 0x72, nameof(Syscall64.ConnectToPort64) },
|
|
||||||
{ 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
|
|
||||||
{ 0x74, nameof(Syscall64.MapProcessMemory64) },
|
|
||||||
{ 0x75, nameof(Syscall64.UnmapProcessMemory64) },
|
|
||||||
{ 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
|
|
||||||
{ 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
|
|
||||||
{ 0x7B, nameof(Syscall64.TerminateProcess64) },
|
|
||||||
{ 0x7D, nameof(Syscall64.CreateResourceLimit64) },
|
|
||||||
{ 0x7E, nameof(Syscall64.SetResourceLimitLimitValue64) }
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (KeyValuePair<int, string> value in svcFuncs64)
|
|
||||||
{
|
|
||||||
SvcTable64[value.Key] = GenerateMethod<Syscall64>(value.Value, SvcFuncMaxArguments64);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<int, string> svcFuncs32 = new Dictionary<int, string>
|
|
||||||
{
|
|
||||||
{ 0x01, nameof(Syscall32.SetHeapSize32) },
|
|
||||||
{ 0x02, nameof(Syscall32.SetMemoryPermission32) },
|
|
||||||
{ 0x03, nameof(Syscall32.SetMemoryAttribute32) },
|
|
||||||
{ 0x04, nameof(Syscall32.MapMemory32) },
|
|
||||||
{ 0x05, nameof(Syscall32.UnmapMemory32) },
|
|
||||||
{ 0x06, nameof(Syscall32.QueryMemory32) },
|
|
||||||
{ 0x07, nameof(Syscall32.ExitProcess32) },
|
|
||||||
{ 0x08, nameof(Syscall32.CreateThread32) },
|
|
||||||
{ 0x09, nameof(Syscall32.StartThread32) },
|
|
||||||
{ 0x0a, nameof(Syscall32.ExitThread32) },
|
|
||||||
{ 0x0b, nameof(Syscall32.SleepThread32) },
|
|
||||||
{ 0x0c, nameof(Syscall32.GetThreadPriority32) },
|
|
||||||
{ 0x0d, nameof(Syscall32.SetThreadPriority32) },
|
|
||||||
{ 0x0e, nameof(Syscall32.GetThreadCoreMask32) },
|
|
||||||
{ 0x0f, nameof(Syscall32.SetThreadCoreMask32) },
|
|
||||||
{ 0x10, nameof(Syscall32.GetCurrentProcessorNumber32) },
|
|
||||||
{ 0x11, nameof(Syscall32.SignalEvent32) },
|
|
||||||
{ 0x12, nameof(Syscall32.ClearEvent32) },
|
|
||||||
{ 0x13, nameof(Syscall32.MapSharedMemory32) },
|
|
||||||
{ 0x14, nameof(Syscall32.UnmapSharedMemory32) },
|
|
||||||
{ 0x15, nameof(Syscall32.CreateTransferMemory32) },
|
|
||||||
{ 0x16, nameof(Syscall32.CloseHandle32) },
|
|
||||||
{ 0x17, nameof(Syscall32.ResetSignal32) },
|
|
||||||
{ 0x18, nameof(Syscall32.WaitSynchronization32) },
|
|
||||||
{ 0x19, nameof(Syscall32.CancelSynchronization32) },
|
|
||||||
{ 0x1a, nameof(Syscall32.ArbitrateLock32) },
|
|
||||||
{ 0x1b, nameof(Syscall32.ArbitrateUnlock32) },
|
|
||||||
{ 0x1c, nameof(Syscall32.WaitProcessWideKeyAtomic32) },
|
|
||||||
{ 0x1d, nameof(Syscall32.SignalProcessWideKey32) },
|
|
||||||
{ 0x1e, nameof(Syscall32.GetSystemTick32) },
|
|
||||||
{ 0x1f, nameof(Syscall32.ConnectToNamedPort32) },
|
|
||||||
{ 0x21, nameof(Syscall32.SendSyncRequest32) },
|
|
||||||
{ 0x22, nameof(Syscall32.SendSyncRequestWithUserBuffer32) },
|
|
||||||
{ 0x24, nameof(Syscall32.GetProcessId32) },
|
|
||||||
{ 0x25, nameof(Syscall32.GetThreadId32) },
|
|
||||||
{ 0x26, nameof(Syscall32.Break32) },
|
|
||||||
{ 0x27, nameof(Syscall32.OutputDebugString32) },
|
|
||||||
{ 0x29, nameof(Syscall32.GetInfo32) },
|
|
||||||
{ 0x2c, nameof(Syscall32.MapPhysicalMemory32) },
|
|
||||||
{ 0x2d, nameof(Syscall32.UnmapPhysicalMemory32) },
|
|
||||||
{ 0x30, nameof(Syscall32.GetResourceLimitLimitValue32) },
|
|
||||||
{ 0x31, nameof(Syscall32.GetResourceLimitCurrentValue32) },
|
|
||||||
{ 0x32, nameof(Syscall32.SetThreadActivity32) },
|
|
||||||
{ 0x33, nameof(Syscall32.GetThreadContext332) },
|
|
||||||
{ 0x34, nameof(Syscall32.WaitForAddress32) },
|
|
||||||
{ 0x35, nameof(Syscall32.SignalToAddress32) },
|
|
||||||
{ 0x36, nameof(Syscall32.SynchronizePreemptionState32) },
|
|
||||||
{ 0x37, nameof(Syscall32.GetResourceLimitPeakValue32) },
|
|
||||||
{ 0x40, nameof(Syscall32.CreateSession32) },
|
|
||||||
{ 0x41, nameof(Syscall32.AcceptSession32) },
|
|
||||||
{ 0x43, nameof(Syscall32.ReplyAndReceive32) },
|
|
||||||
{ 0x45, nameof(Syscall32.CreateEvent32) },
|
|
||||||
{ 0x4b, nameof(Syscall32.CreateCodeMemory32) },
|
|
||||||
{ 0x4c, nameof(Syscall32.ControlCodeMemory32) },
|
|
||||||
{ 0x51, nameof(Syscall32.MapTransferMemory32) },
|
|
||||||
{ 0x52, nameof(Syscall32.UnmapTransferMemory32) },
|
|
||||||
{ 0x5F, nameof(Syscall32.FlushProcessDataCache32) },
|
|
||||||
{ 0x65, nameof(Syscall32.GetProcessList32) },
|
|
||||||
{ 0x6f, nameof(Syscall32.GetSystemInfo32) },
|
|
||||||
{ 0x70, nameof(Syscall32.CreatePort32) },
|
|
||||||
{ 0x71, nameof(Syscall32.ManageNamedPort32) },
|
|
||||||
{ 0x72, nameof(Syscall32.ConnectToPort32) },
|
|
||||||
{ 0x73, nameof(Syscall32.SetProcessMemoryPermission32) },
|
|
||||||
{ 0x74, nameof(Syscall32.MapProcessMemory32) },
|
|
||||||
{ 0x75, nameof(Syscall32.UnmapProcessMemory32) },
|
|
||||||
{ 0x77, nameof(Syscall32.MapProcessCodeMemory32) },
|
|
||||||
{ 0x78, nameof(Syscall32.UnmapProcessCodeMemory32) },
|
|
||||||
{ 0x7B, nameof(Syscall32.TerminateProcess32) },
|
|
||||||
{ 0x7D, nameof(Syscall32.CreateResourceLimit32) },
|
|
||||||
{ 0x7E, nameof(Syscall32.SetResourceLimitLimitValue32) }
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (KeyValuePair<int, string> value in svcFuncs32)
|
|
||||||
{
|
|
||||||
SvcTable32[value.Key] = GenerateMethod<Syscall32>(value.Value, SvcFuncMaxArguments32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<T, ExecutionContext> GenerateMethod<T>(string svcName, int registerCleanCount)
|
|
||||||
{
|
|
||||||
Type[] argTypes = new Type[] { typeof(T), typeof(ExecutionContext) };
|
|
||||||
|
|
||||||
DynamicMethod method = new DynamicMethod(svcName, null, argTypes);
|
|
||||||
|
|
||||||
MethodInfo methodInfo = typeof(T).GetMethod(svcName);
|
|
||||||
|
|
||||||
ParameterInfo[] methodArgs = methodInfo.GetParameters();
|
|
||||||
|
|
||||||
ILGenerator generator = method.GetILGenerator();
|
|
||||||
|
|
||||||
void ConvertToArgType(Type sourceType)
|
|
||||||
{
|
|
||||||
CheckIfTypeIsSupported(sourceType, svcName);
|
|
||||||
|
|
||||||
switch (Type.GetTypeCode(sourceType))
|
|
||||||
{
|
|
||||||
case TypeCode.UInt32: generator.Emit(OpCodes.Conv_U4); break;
|
|
||||||
case TypeCode.Int32: generator.Emit(OpCodes.Conv_I4); break;
|
|
||||||
case TypeCode.UInt16: generator.Emit(OpCodes.Conv_U2); break;
|
|
||||||
case TypeCode.Int16: generator.Emit(OpCodes.Conv_I2); break;
|
|
||||||
case TypeCode.Byte: generator.Emit(OpCodes.Conv_U1); break;
|
|
||||||
case TypeCode.SByte: generator.Emit(OpCodes.Conv_I1); break;
|
|
||||||
|
|
||||||
case TypeCode.Boolean:
|
|
||||||
generator.Emit(OpCodes.Conv_I4);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4_1);
|
|
||||||
generator.Emit(OpCodes.And);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConvertToFieldType(Type sourceType)
|
|
||||||
{
|
|
||||||
CheckIfTypeIsSupported(sourceType, svcName);
|
|
||||||
|
|
||||||
switch (Type.GetTypeCode(sourceType))
|
|
||||||
{
|
|
||||||
case TypeCode.UInt32:
|
|
||||||
case TypeCode.Int32:
|
|
||||||
case TypeCode.UInt16:
|
|
||||||
case TypeCode.Int16:
|
|
||||||
case TypeCode.Byte:
|
|
||||||
case TypeCode.SByte:
|
|
||||||
case TypeCode.Boolean:
|
|
||||||
generator.Emit(OpCodes.Conv_U8);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RAttribute GetRegisterAttribute(ParameterInfo parameterInfo)
|
|
||||||
{
|
|
||||||
RAttribute argumentAttribute = (RAttribute)parameterInfo.GetCustomAttribute(typeof(RAttribute));
|
|
||||||
|
|
||||||
if (argumentAttribute == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Method \"{svcName}\" is missing a {typeof(RAttribute).Name} attribute on parameter \"{parameterInfo.Name}\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
return argumentAttribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For functions returning output values, the first registers
|
|
||||||
// are used to hold pointers where the value will be stored,
|
|
||||||
// so they can't be used to pass argument and we must
|
|
||||||
// skip them.
|
|
||||||
int byRefArgsCount = 0;
|
|
||||||
|
|
||||||
for (int index = 0; index < methodArgs.Length; index++)
|
|
||||||
{
|
|
||||||
if (methodArgs[index].ParameterType.IsByRef)
|
|
||||||
{
|
|
||||||
byRefArgsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BindingFlags staticNonPublic = BindingFlags.NonPublic | BindingFlags.Static;
|
|
||||||
|
|
||||||
// Print all the arguments for debugging purposes.
|
|
||||||
int inputArgsCount = methodArgs.Length - byRefArgsCount;
|
|
||||||
|
|
||||||
if (inputArgsCount != 0)
|
|
||||||
{
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, inputArgsCount);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Newarr, typeof(object));
|
|
||||||
|
|
||||||
string argsFormat = svcName;
|
|
||||||
|
|
||||||
for (int index = 0; index < methodArgs.Length; index++)
|
|
||||||
{
|
|
||||||
Type argType = methodArgs[index].ParameterType;
|
|
||||||
|
|
||||||
// Ignore out argument for printing
|
|
||||||
if (argType.IsByRef)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]);
|
|
||||||
|
|
||||||
argsFormat += $" {methodArgs[index].Name}: 0x{{{index}:X8}},";
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Dup);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, index);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index);
|
|
||||||
|
|
||||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, info);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Box, typeof(ulong));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Stelem_Ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
argsFormat = argsFormat.Substring(0, argsFormat.Length - 1);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldstr, argsFormat);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
generator.Emit(OpCodes.Ldnull);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldstr, svcName);
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodInfo printArgsMethod = typeof(SyscallTable).GetMethod(nameof(PrintArguments), staticNonPublic);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, printArgsMethod);
|
|
||||||
|
|
||||||
// Call the SVC function handler.
|
|
||||||
generator.Emit(OpCodes.Ldarg_0);
|
|
||||||
|
|
||||||
List<(LocalBuilder, RAttribute)> locals = new List<(LocalBuilder, RAttribute)>();
|
|
||||||
|
|
||||||
for (int index = 0; index < methodArgs.Length; index++)
|
|
||||||
{
|
|
||||||
Type argType = methodArgs[index].ParameterType;
|
|
||||||
RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]);
|
|
||||||
|
|
||||||
if (argType.IsByRef)
|
|
||||||
{
|
|
||||||
argType = argType.GetElementType();
|
|
||||||
|
|
||||||
LocalBuilder local = generator.DeclareLocal(argType);
|
|
||||||
|
|
||||||
locals.Add((local, registerAttribute));
|
|
||||||
|
|
||||||
if (!methodArgs[index].IsOut)
|
|
||||||
{
|
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index);
|
|
||||||
|
|
||||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, info);
|
|
||||||
|
|
||||||
ConvertToArgType(argType);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Stloc, local);
|
|
||||||
}
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldloca, local);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index);
|
|
||||||
|
|
||||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, info);
|
|
||||||
|
|
||||||
ConvertToArgType(argType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, methodInfo);
|
|
||||||
|
|
||||||
Type retType = methodInfo.ReturnType;
|
|
||||||
|
|
||||||
// Print result code.
|
|
||||||
if (retType == typeof(KernelResult))
|
|
||||||
{
|
|
||||||
MethodInfo printResultMethod = typeof(SyscallTable).GetMethod(nameof(PrintResult), staticNonPublic);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Dup);
|
|
||||||
generator.Emit(OpCodes.Ldstr, svcName);
|
|
||||||
generator.Emit(OpCodes.Call, printResultMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint registerInUse = 0;
|
|
||||||
|
|
||||||
// Save return value into register X0 (when the method has a return value).
|
|
||||||
if (retType != typeof(void))
|
|
||||||
{
|
|
||||||
CheckIfTypeIsSupported(retType, svcName);
|
|
||||||
|
|
||||||
LocalBuilder tempLocal = generator.DeclareLocal(retType);
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Stloc, tempLocal);
|
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, 0);
|
|
||||||
generator.Emit(OpCodes.Ldloc, tempLocal);
|
|
||||||
|
|
||||||
ConvertToFieldType(retType);
|
|
||||||
|
|
||||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, info);
|
|
||||||
|
|
||||||
registerInUse |= 1u << 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < locals.Count; index++)
|
|
||||||
{
|
|
||||||
(LocalBuilder local, RAttribute attribute) = locals[index];
|
|
||||||
|
|
||||||
if ((registerInUse & (1u << attribute.Index)) != 0)
|
|
||||||
{
|
|
||||||
throw new InvalidSvcException($"Method \"{svcName}\" has conflicting output values at register index \"{attribute.Index}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, attribute.Index);
|
|
||||||
generator.Emit(OpCodes.Ldloc, local);
|
|
||||||
|
|
||||||
ConvertToFieldType(local.LocalType);
|
|
||||||
|
|
||||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, info);
|
|
||||||
|
|
||||||
registerInUse |= 1u << attribute.Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero out the remaining unused registers.
|
|
||||||
for (int i = 0; i < registerCleanCount; i++)
|
|
||||||
{
|
|
||||||
if ((registerInUse & (1u << i)) != 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
|
||||||
generator.Emit(OpCodes.Ldc_I4, i);
|
|
||||||
generator.Emit(OpCodes.Ldc_I8, 0L);
|
|
||||||
|
|
||||||
MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX));
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ret);
|
|
||||||
|
|
||||||
return method.CreateDelegate<Action<T, ExecutionContext>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CheckIfTypeIsSupported(Type type, string svcName)
|
|
||||||
{
|
|
||||||
switch (Type.GetTypeCode(type))
|
|
||||||
{
|
|
||||||
case TypeCode.UInt64:
|
|
||||||
case TypeCode.Int64:
|
|
||||||
case TypeCode.UInt32:
|
|
||||||
case TypeCode.Int32:
|
|
||||||
case TypeCode.UInt16:
|
|
||||||
case TypeCode.Int16:
|
|
||||||
case TypeCode.Byte:
|
|
||||||
case TypeCode.SByte:
|
|
||||||
case TypeCode.Boolean:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidSvcException($"Method \"{svcName}\" has a invalid ref type \"{type.Name}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintArguments(object[] argValues, string formatOrSvcName)
|
|
||||||
{
|
|
||||||
if (argValues != null)
|
|
||||||
{
|
|
||||||
Logger.Trace?.Print(LogClass.KernelSvc, string.Format(formatOrSvcName, argValues));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Trace?.Print(LogClass.KernelSvc, formatOrSvcName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintResult(KernelResult result, string svcName)
|
|
||||||
{
|
|
||||||
if (result != KernelResult.Success &&
|
|
||||||
result != KernelResult.TimedOut &&
|
|
||||||
result != KernelResult.Cancelled &&
|
|
||||||
result != KernelResult.PortRemoteClosed &&
|
|
||||||
result != KernelResult.InvalidState)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.KernelSvc, $"{svcName} returned error {result}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Trace?.Print(LogClass.KernelSvc, $"{svcName} returned result {result}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public Thread HostThread { get; private set; }
|
public Thread HostThread { get; private set; }
|
||||||
|
|
||||||
public ARMeilleure.State.ExecutionContext Context { get; private set; }
|
public IExecutionContext Context { get; private set; }
|
||||||
|
|
||||||
public KThreadContext ThreadContext { get; private set; }
|
public KThreadContext ThreadContext { get; private set; }
|
||||||
|
|
||||||
@ -115,9 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public bool WaitingInArbitration { get; set; }
|
public bool WaitingInArbitration { get; set; }
|
||||||
|
|
||||||
public long LastPc { get; set; }
|
private object _activityOperationLock;
|
||||||
|
|
||||||
private object ActivityOperationLock = new object();
|
|
||||||
|
|
||||||
public KThread(KernelContext context) : base(context)
|
public KThread(KernelContext context) : base(context)
|
||||||
{
|
{
|
||||||
@ -128,6 +126,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
_mutexWaiters = new LinkedList<KThread>();
|
_mutexWaiters = new LinkedList<KThread>();
|
||||||
_pinnedWaiters = new LinkedList<KThread>();
|
_pinnedWaiters = new LinkedList<KThread>();
|
||||||
|
|
||||||
|
_activityOperationLock = new object();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult Initialize(
|
public KernelResult Initialize(
|
||||||
@ -192,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
HostThread = new Thread(ThreadStart);
|
HostThread = new Thread(ThreadStart);
|
||||||
|
|
||||||
Context = CpuContext.CreateExecutionContext();
|
Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext();
|
||||||
|
|
||||||
Context.IsAarch32 = !is64Bits;
|
Context.IsAarch32 = !is64Bits;
|
||||||
|
|
||||||
@ -208,8 +208,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
Context.SetX(13, (uint)stackTop);
|
Context.SetX(13, (uint)stackTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.CntfrqEl0 = 19200000;
|
Context.TpidrroEl0 = (long)_tlsAddress;
|
||||||
Context.Tpidr = (long)_tlsAddress;
|
|
||||||
|
|
||||||
ThreadUid = KernelContext.NewThreadUid();
|
ThreadUid = KernelContext.NewThreadUid();
|
||||||
|
|
||||||
@ -221,7 +220,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (owner != null)
|
if (owner != null)
|
||||||
{
|
{
|
||||||
owner.SubscribeThreadEventHandlers(Context);
|
|
||||||
owner.AddThread(this);
|
owner.AddThread(this);
|
||||||
|
|
||||||
if (owner.IsPaused)
|
if (owner.IsPaused)
|
||||||
@ -538,7 +536,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public KernelResult SetActivity(bool pause)
|
public KernelResult SetActivity(bool pause)
|
||||||
{
|
{
|
||||||
lock (ActivityOperationLock)
|
lock (_activityOperationLock)
|
||||||
{
|
{
|
||||||
KernelResult result = KernelResult.Success;
|
KernelResult result = KernelResult.Success;
|
||||||
|
|
||||||
@ -634,7 +632,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
context = default;
|
context = default;
|
||||||
|
|
||||||
lock (ActivityOperationLock)
|
lock (_activityOperationLock)
|
||||||
{
|
{
|
||||||
KernelContext.CriticalSection.Enter();
|
KernelContext.CriticalSection.Enter();
|
||||||
|
|
||||||
@ -656,7 +654,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint GetPsr(ARMeilleure.State.ExecutionContext context)
|
private static uint GetPsr(IExecutionContext context)
|
||||||
{
|
{
|
||||||
return context.Pstate & 0xFF0FFE20;
|
return context.Pstate & 0xFF0FFE20;
|
||||||
}
|
}
|
||||||
@ -683,9 +681,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
context.Fp = Context.GetX(29);
|
context.Fp = Context.GetX(29);
|
||||||
context.Lr = Context.GetX(30);
|
context.Lr = Context.GetX(30);
|
||||||
context.Sp = Context.GetX(31);
|
context.Sp = Context.GetX(31);
|
||||||
context.Pc = (ulong)LastPc;
|
context.Pc = Context.Pc;
|
||||||
context.Pstate = GetPsr(Context);
|
context.Pstate = GetPsr(Context);
|
||||||
context.Tpidr = (ulong)Context.Tpidr;
|
context.Tpidr = (ulong)Context.TpidrroEl0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -699,9 +697,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
context.FpuRegisters[i] = Context.GetV(i);
|
context.FpuRegisters[i] = Context.GetV(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Pc = (uint)LastPc;
|
context.Pc = (uint)Context.Pc;
|
||||||
context.Pstate = GetPsr(Context);
|
context.Pstate = GetPsr(Context);
|
||||||
context.Tpidr = (uint)Context.Tpidr;
|
context.Tpidr = (uint)Context.TpidrroEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Fpcr = (uint)Context.Fpcr;
|
context.Fpcr = (uint)Context.Fpcr;
|
||||||
@ -743,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public KernelResult SetCoreAndAffinityMask(int newCore, ulong newAffinityMask)
|
public KernelResult SetCoreAndAffinityMask(int newCore, ulong newAffinityMask)
|
||||||
{
|
{
|
||||||
lock (ActivityOperationLock)
|
lock (_activityOperationLock)
|
||||||
{
|
{
|
||||||
KernelContext.CriticalSection.Enter();
|
KernelContext.CriticalSection.Enter();
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion];
|
KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion];
|
||||||
|
|
||||||
KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList);
|
KernelResult result = region.AllocatePages(out KPageList pageList, (ulong)codePagesCount);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
KProcess process = new KProcess(context);
|
KProcess process = new KProcess(context);
|
||||||
|
|
||||||
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
|
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
|
||||||
|
|
||||||
result = process.InitializeKip(
|
result = process.InitializeKip(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
@ -264,7 +264,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
|
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
|
||||||
|
|
||||||
result = process.Initialize(
|
result = process.Initialize(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
|
@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{result}");
|
Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user