Compare commits
25 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 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
- os: windows-latest
|
||||
OS_NAME: Windows x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||
RELEASE_ZIP_OS_NAME: win_x64
|
||||
|
||||
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`);
|
||||
}
|
||||
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_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||
for (const art of artifacts) {
|
||||
if(art.name.includes('Debug')) {
|
||||
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)`;
|
||||
} else {
|
||||
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_debug_artifacts += `\n</details>`;
|
||||
body += hidden_avalonia_artifacts;
|
||||
body += hidden_headless_artifacts;
|
||||
body += hidden_debug_artifacts;
|
||||
|
||||
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -51,9 +51,9 @@ jobs:
|
||||
run: "mkdir release_output"
|
||||
- name: Publish Windows
|
||||
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 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 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/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_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_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
|
||||
run: |
|
||||
pushd publish_windows
|
||||
|
@ -33,13 +33,13 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
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_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_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_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr)); 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_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_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_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_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); 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;
|
||||
}
|
||||
|
||||
public static ulong GetTpidr()
|
||||
public static ulong GetTpidrroEl0()
|
||||
{
|
||||
return (ulong)GetContext().Tpidr;
|
||||
return (ulong)GetContext().TpidrroEl0;
|
||||
}
|
||||
|
||||
public static uint GetTpidr32()
|
||||
{
|
||||
return (uint)GetContext().Tpidr;
|
||||
return (uint)GetContext().TpidrroEl0;
|
||||
}
|
||||
|
||||
public static ulong GetCntfrqEl0()
|
||||
|
@ -14,10 +14,11 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
public byte Kind;
|
||||
public byte Type;
|
||||
public byte SymbolType;
|
||||
public byte Padding; // Unused space.
|
||||
public ushort AssignmentsCount;
|
||||
public ushort AssignmentsCapacity;
|
||||
public ushort UsesCount;
|
||||
public ushort UsesCapacity;
|
||||
public uint UsesCount;
|
||||
public uint UsesCapacity;
|
||||
public Operation* Assignments;
|
||||
public Operation* Uses;
|
||||
public ulong Value;
|
||||
@ -84,11 +85,11 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
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 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);
|
||||
}
|
||||
|
||||
|
||||
if (index != default)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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()
|
||||
{
|
||||
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 System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
@ -14,34 +13,22 @@ namespace ARMeilleure.State
|
||||
|
||||
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 ulong CntfrqEl0 { get; set; }
|
||||
public ulong CntpctEl0
|
||||
{
|
||||
get
|
||||
{
|
||||
double ticks = _tickCounter.ElapsedTicks * _hostTickFreq;
|
||||
|
||||
return (ulong)(ticks * CntfrqEl0);
|
||||
}
|
||||
}
|
||||
public ulong CntfrqEl0 => _counter.Frequency;
|
||||
public ulong CntpctEl0 => _counter.Counter;
|
||||
|
||||
// CNTVCT_EL0 = CNTPCT_EL0 - CNTVOFF_EL2
|
||||
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
||||
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 Tpidr { get; set; }
|
||||
public long TpidrroEl0 { get; set; }
|
||||
|
||||
public uint Pstate
|
||||
{
|
||||
@ -78,35 +65,38 @@ namespace ARMeilleure.State
|
||||
private set => _nativeContext.SetRunning(value);
|
||||
}
|
||||
|
||||
public event EventHandler<EventArgs> Interrupt;
|
||||
public event EventHandler<InstExceptionEventArgs> Break;
|
||||
public event EventHandler<InstExceptionEventArgs> SupervisorCall;
|
||||
public event EventHandler<InstUndefinedEventArgs> Undefined;
|
||||
private readonly ExceptionCallbackNoArgs _interruptCallback;
|
||||
private readonly ExceptionCallback _breakCallback;
|
||||
private readonly ExceptionCallback _supervisorCallback;
|
||||
private readonly ExceptionCallback _undefinedCallback;
|
||||
|
||||
static ExecutionContext()
|
||||
{
|
||||
_hostTickFreq = 1.0 / Stopwatch.Frequency;
|
||||
|
||||
_tickCounter = new Stopwatch();
|
||||
_tickCounter.Start();
|
||||
}
|
||||
|
||||
public ExecutionContext(IJitMemoryAllocator allocator)
|
||||
public ExecutionContext(
|
||||
IJitMemoryAllocator allocator,
|
||||
ICounter counter,
|
||||
ExceptionCallbackNoArgs interruptCallback = null,
|
||||
ExceptionCallback breakCallback = null,
|
||||
ExceptionCallback supervisorCallback = null,
|
||||
ExceptionCallback undefinedCallback = null)
|
||||
{
|
||||
_nativeContext = new NativeContext(allocator);
|
||||
_counter = counter;
|
||||
_interruptCallback = interruptCallback;
|
||||
_breakCallback = breakCallback;
|
||||
_supervisorCallback = supervisorCallback;
|
||||
_undefinedCallback = undefinedCallback;
|
||||
|
||||
Running = true;
|
||||
|
||||
_nativeContext.SetCounter(MinCountForCheck);
|
||||
}
|
||||
|
||||
public ulong GetX(int index) => _nativeContext.GetX(index);
|
||||
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
|
||||
public ulong GetX(int index) => _nativeContext.GetX(index);
|
||||
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 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 bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
||||
@ -118,7 +108,7 @@ namespace ARMeilleure.State
|
||||
{
|
||||
_interrupted = false;
|
||||
|
||||
Interrupt?.Invoke(this, EventArgs.Empty);
|
||||
_interruptCallback?.Invoke(this);
|
||||
}
|
||||
|
||||
_nativeContext.SetCounter(MinCountForCheck);
|
||||
@ -131,17 +121,17 @@ namespace ARMeilleure.State
|
||||
|
||||
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)
|
||||
{
|
||||
SupervisorCall?.Invoke(this, new InstExceptionEventArgs(address, imm));
|
||||
_supervisorCallback?.Invoke(this, address, imm);
|
||||
}
|
||||
|
||||
internal void OnUndefined(ulong address, int opCode)
|
||||
{
|
||||
Undefined?.Invoke(this, new InstUndefinedEventArgs(address, opCode));
|
||||
_undefinedCallback?.Invoke(this, address, opCode);
|
||||
}
|
||||
|
||||
public void StopRunning()
|
||||
@ -151,16 +141,6 @@ namespace ARMeilleure.State
|
||||
_nativeContext.SetCounter(0);
|
||||
}
|
||||
|
||||
public static void SuspendCounter()
|
||||
{
|
||||
_tickCounter.Stop();
|
||||
}
|
||||
|
||||
public static void ResumeCounter()
|
||||
{
|
||||
_tickCounter.Start();
|
||||
}
|
||||
|
||||
public void 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;
|
||||
}
|
||||
|
||||
public ulong GetPc()
|
||||
{
|
||||
// TODO: More precise tracking of PC value.
|
||||
return GetStorage().DispatchAddress;
|
||||
}
|
||||
|
||||
public unsafe ulong GetX(int index)
|
||||
{
|
||||
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.GetFunctionAddress)));
|
||||
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.GetTpidrEl0)));
|
||||
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 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 BackupDir = "1";
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -15,11 +15,11 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>libsoundio.dll</TargetPath>
|
||||
</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>
|
||||
<TargetPath>libsoundio.dylib</TargetPath>
|
||||
</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>
|
||||
<TargetPath>libsoundio.so</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
|
@ -523,9 +523,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
private ulong GetSystemTicks()
|
||||
{
|
||||
double ticks = ARMeilleure.State.ExecutionContext.ElapsedTicks * ARMeilleure.State.ExecutionContext.TickFrequency;
|
||||
|
||||
return (ulong)(ticks * Constants.TargetTimerFrequency);
|
||||
return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
|
||||
}
|
||||
|
||||
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.Parameter;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@ -77,6 +78,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// </summary>
|
||||
private IHardwareDeviceDriver _deviceDriver;
|
||||
|
||||
/// <summary>
|
||||
/// Tick source used to measure elapsed time.
|
||||
/// </summary>
|
||||
public ITickSource TickSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="AudioProcessor"/> instance associated to this manager.
|
||||
/// </summary>
|
||||
@ -90,9 +96,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <summary>
|
||||
/// Create a new <see cref="AudioRendererManager"/>.
|
||||
/// </summary>
|
||||
public AudioRendererManager()
|
||||
/// <param name="tickSource">Tick source used to measure elapsed time.</param>
|
||||
public AudioRendererManager(ITickSource tickSource)
|
||||
{
|
||||
Processor = new AudioProcessor();
|
||||
TickSource = tickSource;
|
||||
_sessionIds = new int[Constants.AudioRendererSessionCountMax];
|
||||
_sessions = new AudioRenderSystem[Constants.AudioRendererSessionCountMax];
|
||||
_activeSessionCount = 0;
|
||||
|
@ -177,7 +177,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
double scale = Program.WindowScaleFactor;
|
||||
double scale = _parent.PlatformImpl.RenderScaling;
|
||||
_renderer.Window.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
|
||||
}
|
||||
}
|
||||
@ -809,7 +809,7 @@ namespace Ryujinx.Ava
|
||||
Width = (int)Renderer.Bounds.Width;
|
||||
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(() =>
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ namespace Ryujinx.Modules
|
||||
string assetState = (string)asset["state"];
|
||||
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;
|
||||
|
||||
|
@ -137,7 +137,6 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
|
||||
// Make process DPI aware for proper window sizing on high-res screens.
|
||||
ForceDpiAware.Windows();
|
||||
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
||||
|
||||
// Delete backup files after updating.
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>1.0.0-dirty</Version>
|
||||
|
@ -73,10 +73,13 @@ namespace Ryujinx.Ava.Ui.Controls
|
||||
{
|
||||
SizeChanged?.Invoke(this, rect.Size);
|
||||
|
||||
RenderSize = rect.Size * Program.WindowScaleFactor;
|
||||
if (!rect.IsEmpty)
|
||||
{
|
||||
RenderSize = rect.Size * VisualRoot.RenderScaling;
|
||||
|
||||
_glDrawOperation?.Dispose();
|
||||
_glDrawOperation = new GlDrawOperation(this);
|
||||
_glDrawOperation?.Dispose();
|
||||
_glDrawOperation = new GlDrawOperation(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
|
@ -123,10 +123,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
CheckLaunchState();
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
Program.WindowScaleFactor = this.PlatformImpl.RenderScaling;
|
||||
}
|
||||
_rendererWaitEvent = new AutoResetEvent(false);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,17 @@ namespace Ryujinx.Common
|
||||
|
||||
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)
|
||||
@ -42,7 +52,17 @@ namespace Ryujinx.Common
|
||||
|
||||
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)
|
||||
|
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 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 Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve);
|
@ -2,9 +2,9 @@
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
class JitMemoryBlock : IJitMemoryBlock
|
||||
public class JitMemoryBlock : IJitMemoryBlock
|
||||
{
|
||||
private readonly MemoryBlock _impl;
|
||||
|
@ -10,7 +10,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager.
|
@ -8,12 +8,12 @@ using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||
/// </summary>
|
||||
public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
public sealed class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
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 ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
||||
void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color);
|
||||
|
||||
void ClearRenderTargetDepthStencil(
|
||||
int layer,
|
||||
float depthValue,
|
||||
bool depthMask,
|
||||
int stencilValue,
|
||||
|
@ -4,19 +4,21 @@
|
||||
{
|
||||
public CommandType CommandType => CommandType.ClearRenderTargetColor;
|
||||
private int _index;
|
||||
private int _layer;
|
||||
private uint _componentMask;
|
||||
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;
|
||||
_layer = layer;
|
||||
_componentMask = componentMask;
|
||||
_color = color;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil;
|
||||
private int _layer;
|
||||
private float _depthValue;
|
||||
private bool _depthMask;
|
||||
private int _stencilValue;
|
||||
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;
|
||||
_depthMask = depthMask;
|
||||
_stencilValue = stencilValue;
|
||||
@ -18,7 +20,7 @@
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL
|
||||
CubemapArray,
|
||||
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.SetComputeUniformBufferBindings(info.CBuffers);
|
||||
|
||||
int maxTextureBinding = -1;
|
||||
int maxImageBinding = -1;
|
||||
|
||||
TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count);
|
||||
|
||||
for (int index = 0; index < info.Textures.Count; index++)
|
||||
@ -202,6 +205,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||
descriptor.CbufSlot,
|
||||
descriptor.HandleIndex,
|
||||
descriptor.Flags);
|
||||
|
||||
if (descriptor.Binding > maxTextureBinding)
|
||||
{
|
||||
maxTextureBinding = descriptor.Binding;
|
||||
}
|
||||
}
|
||||
|
||||
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count);
|
||||
@ -220,9 +228,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||
descriptor.CbufSlot,
|
||||
descriptor.HandleIndex,
|
||||
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();
|
||||
|
||||
_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 _instancedIndexed;
|
||||
private bool _instancedIndexedInline;
|
||||
|
||||
private int _instancedFirstIndex;
|
||||
private int _instancedFirstVertex;
|
||||
@ -134,13 +135,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
_instancedDrawPending = true;
|
||||
|
||||
int ibCount = _drawState.IbStreamer.InlineIndexCount;
|
||||
|
||||
_instancedIndexed = _drawState.DrawIndexed;
|
||||
_instancedIndexedInline = ibCount != 0;
|
||||
|
||||
_instancedFirstIndex = firstIndex;
|
||||
_instancedFirstVertex = (int)_state.State.FirstVertex;
|
||||
_instancedFirstInstance = (int)_state.State.FirstInstance;
|
||||
|
||||
_instancedIndexCount = indexCount;
|
||||
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
|
||||
|
||||
var drawState = _state.State.VertexBufferDrawState;
|
||||
|
||||
@ -451,8 +455,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
_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(
|
||||
_instancedIndexCount,
|
||||
_instanceIndex + 1,
|
||||
@ -491,8 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
}
|
||||
|
||||
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
|
||||
// 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);
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
|
||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color);
|
||||
}
|
||||
|
||||
if (clearDepth || clearStencil)
|
||||
@ -588,6 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
||||
layer,
|
||||
depthValue,
|
||||
clearDepth,
|
||||
stencilValue,
|
||||
|
@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
public bool HasInlineIndexData => _inlineIndexCount != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Total numbers of indices that have been pushed.
|
||||
/// </summary>
|
||||
public int InlineIndexCount => _inlineIndexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handle for the host buffer currently holding the inline index buffer data.
|
||||
/// </summary>
|
||||
|
@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
private enum ReportCounterType
|
||||
{
|
||||
Zero = 0,
|
||||
Payload = 0,
|
||||
InputVertices = 1,
|
||||
InputPrimitives = 3,
|
||||
VertexShaderInvocations = 5,
|
||||
@ -169,8 +169,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ReportCounterType.Zero:
|
||||
resultHandler(null, 0);
|
||||
case ReportCounterType.Payload:
|
||||
resultHandler(null, (ulong)_state.State.SemaphorePayload);
|
||||
break;
|
||||
case ReportCounterType.SamplesPassed:
|
||||
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.
|
||||
if (_shaderSpecState != null)
|
||||
{
|
||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState()))
|
||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false))
|
||||
{
|
||||
ForceShaderUpdate();
|
||||
}
|
||||
@ -275,7 +275,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
UpdateStorageBuffers();
|
||||
|
||||
_channel.TextureManager.CommitGraphicsBindings();
|
||||
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState))
|
||||
{
|
||||
// Shader must be reloaded.
|
||||
UpdateShaderState();
|
||||
}
|
||||
|
||||
_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.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
|
||||
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
||||
{
|
||||
var memoryManager = _channel.MemoryManager;
|
||||
var rtControl = _state.State.RtControl;
|
||||
@ -399,7 +405,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||
memoryManager,
|
||||
colorState,
|
||||
_vtgWritesRtLayer,
|
||||
_vtgWritesRtLayer || layered,
|
||||
samplesInX,
|
||||
samplesInY,
|
||||
sizeHint);
|
||||
@ -433,6 +439,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
memoryManager,
|
||||
dsState,
|
||||
dsSize,
|
||||
_vtgWritesRtLayer || layered,
|
||||
samplesInX,
|
||||
samplesInY,
|
||||
sizeHint);
|
||||
@ -1148,6 +1155,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
return;
|
||||
}
|
||||
|
||||
int maxTextureBinding = -1;
|
||||
int maxImageBinding = -1;
|
||||
|
||||
Span<TextureBindingInfo> textureBindings = _channel.TextureManager.RentGraphicsTextureBindings(stage, info.Textures.Count);
|
||||
|
||||
if (info.UsesRtLayer)
|
||||
@ -1167,6 +1177,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
descriptor.CbufSlot,
|
||||
descriptor.HandleIndex,
|
||||
descriptor.Flags);
|
||||
|
||||
if (descriptor.Binding > maxTextureBinding)
|
||||
{
|
||||
maxTextureBinding = descriptor.Binding;
|
||||
}
|
||||
}
|
||||
|
||||
TextureBindingInfo[] imageBindings = _channel.TextureManager.RentGraphicsImageBindings(stage, info.Images.Count);
|
||||
@ -1185,8 +1200,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
descriptor.CbufSlot,
|
||||
descriptor.HandleIndex,
|
||||
descriptor.Flags);
|
||||
|
||||
if (descriptor.Binding > maxImageBinding)
|
||||
{
|
||||
maxImageBinding = descriptor.Binding;
|
||||
}
|
||||
}
|
||||
|
||||
_channel.TextureManager.SetGraphicsMaxBindings(maxTextureBinding, maxImageBinding);
|
||||
|
||||
_channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
|
||||
_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.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
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>
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Ryujinx.Cpu.Tracking;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
@ -16,6 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
protected GpuContext Context;
|
||||
protected PhysicalMemory PhysicalMemory;
|
||||
protected int SequenceNumber;
|
||||
protected int ModifiedSequenceNumber;
|
||||
|
||||
protected T1[] Items;
|
||||
protected T2[] DescriptorCache;
|
||||
@ -41,6 +43,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
private readonly CpuMultiRegionHandle _memoryTracking;
|
||||
private readonly Action<ulong, ulong> _modifiedDelegate;
|
||||
|
||||
private int _modifiedSequenceOffset;
|
||||
private bool _modified;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU resource pool.
|
||||
/// </summary>
|
||||
@ -79,6 +84,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
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>
|
||||
/// Gets the GPU resource with the given ID.
|
||||
/// </summary>
|
||||
@ -93,7 +108,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public void SynchronizeMemory()
|
||||
{
|
||||
_modified = false;
|
||||
_memoryTracking.QueryModified(_modifiedDelegate);
|
||||
|
||||
if (_modified)
|
||||
{
|
||||
UpdateModifiedSequence();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -103,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="mSize">Size of the modified region</param>
|
||||
private void RegionModified(ulong mAddress, ulong mSize)
|
||||
{
|
||||
_modified = true;
|
||||
|
||||
if (mAddress < Address)
|
||||
{
|
||||
mAddress = Address;
|
||||
@ -118,6 +141,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
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>
|
||||
/// An action to be performed when a precise memory access occurs to this resource.
|
||||
/// Makes sure that the dirty flags are checked.
|
||||
@ -129,6 +161,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
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--;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
class Sampler : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the sampler is disposed, false otherwise.
|
||||
/// </summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Host sampler object.
|
||||
/// </summary>
|
||||
@ -101,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
|
||||
_hostSampler.Dispose();
|
||||
_anisoSampler?.Dispose();
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
Items[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateModifiedSequence();
|
||||
}
|
||||
|
||||
SequenceNumber = Context.SequenceNumber;
|
||||
@ -71,6 +73,39 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
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>
|
||||
/// Implementation of the sampler pool range invalidation.
|
||||
/// </summary>
|
||||
|
@ -100,6 +100,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
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 _layers;
|
||||
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="layerSize">Layer size on the given texture</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="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>
|
||||
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;
|
||||
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
|
||||
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;
|
||||
}
|
||||
|
||||
if (!msTargetCompatible)
|
||||
{
|
||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
|
||||
|
||||
if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)
|
||||
{
|
||||
result = TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
result = TextureViewCompatibility.Incompatible;
|
||||
}
|
||||
|
||||
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)
|
||||
@ -1417,6 +1412,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
DisposeTextures();
|
||||
|
||||
HostTexture = hostTexture;
|
||||
InvalidatedSequence++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1545,6 +1541,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
_poolOwners.Clear();
|
||||
}
|
||||
|
||||
InvalidatedSequence++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,8 +1,12 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
@ -31,21 +35,30 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
private readonly TextureBindingInfo[][] _textureBindings;
|
||||
private readonly TextureBindingInfo[][] _imageBindings;
|
||||
|
||||
private struct TextureStatePerStage
|
||||
private struct TextureState
|
||||
{
|
||||
public ITexture Texture;
|
||||
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 readonly TextureStatePerStage[][] _imageState;
|
||||
private TextureState[] _textureState;
|
||||
private TextureState[] _imageState;
|
||||
|
||||
private int[] _textureBindingsCount;
|
||||
private int[] _imageBindingsCount;
|
||||
|
||||
private int _textureBufferIndex;
|
||||
private int _texturePoolSequence;
|
||||
private int _samplerPoolSequence;
|
||||
|
||||
private bool _rebind;
|
||||
private int _textureBufferIndex;
|
||||
|
||||
private readonly float[] _scales;
|
||||
private bool _scaleChanged;
|
||||
@ -72,8 +85,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_textureBindings = new TextureBindingInfo[stages][];
|
||||
_imageBindings = new TextureBindingInfo[stages][];
|
||||
|
||||
_textureState = new TextureStatePerStage[stages][];
|
||||
_imageState = new TextureStatePerStage[stages][];
|
||||
_textureState = new TextureState[InitialTextureStateSize];
|
||||
_imageState = new TextureState[InitialImageStateSize];
|
||||
|
||||
_textureBindingsCount = new int[stages];
|
||||
_imageBindingsCount = new int[stages];
|
||||
@ -82,9 +95,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
_textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
|
||||
_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)
|
||||
{
|
||||
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;
|
||||
@ -126,15 +127,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
if (count > _imageBindings[stage].Length)
|
||||
{
|
||||
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;
|
||||
@ -142,6 +134,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
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>
|
||||
/// Sets the textures constant buffer index.
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <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="stage">The active shader stage</param>
|
||||
/// <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;
|
||||
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;
|
||||
texture.BlacklistScale();
|
||||
@ -323,7 +333,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// Ensures that the bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </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;
|
||||
|
||||
@ -331,10 +343,38 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
||||
: 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)
|
||||
{
|
||||
CommitTextureBindings(texturePool, ShaderStage.Compute, 0);
|
||||
CommitImageBindings (texturePool, ShaderStage.Compute, 0);
|
||||
specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
||||
specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -342,14 +382,57 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
int stageIndex = (int)stage - 1;
|
||||
|
||||
CommitTextureBindings(texturePool, stage, stageIndex);
|
||||
CommitImageBindings (texturePool, stage, stageIndex);
|
||||
specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState);
|
||||
specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
@ -358,13 +441,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
/// <param name="pool">The current texture pool</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>
|
||||
private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex)
|
||||
/// <param name="stageIndex">The stage number of the specified shader stage</param
|
||||
/// <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];
|
||||
if (textureCount == 0)
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
var samplerPool = _samplerPool;
|
||||
@ -372,17 +458,27 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
if (pool == null)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index];
|
||||
TextureUsageFlags usageFlags = bindingInfo.Flags;
|
||||
|
||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
|
||||
|
||||
int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
|
||||
int textureId = UnpackTextureId(packedId);
|
||||
UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
|
||||
|
||||
int packedId = TextureHandle.ReadPackedId(bindingInfo.Handle, cachedTextureBuffer, cachedSamplerBuffer);
|
||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||
int samplerId;
|
||||
|
||||
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
|
||||
@ -391,10 +487,42 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
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);
|
||||
|
||||
@ -407,30 +535,38 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
_textureState[stageIndex][index].Texture = hostTexture;
|
||||
state.Texture = hostTexture;
|
||||
state.ScaleIndex = index;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
|
||||
}
|
||||
|
||||
Sampler sampler = samplerPool?.Get(samplerId);
|
||||
state.CachedSampler = sampler;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
state.CachedTexture = texture;
|
||||
state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return specStateMatches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -440,38 +576,90 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="pool">The current texture pool</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>
|
||||
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];
|
||||
if (imageCount == 0)
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pool == null)
|
||||
{
|
||||
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.
|
||||
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++)
|
||||
{
|
||||
TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index];
|
||||
TextureUsageFlags usageFlags = bindingInfo.Flags;
|
||||
int scaleIndex = baseScaleIndex + index;
|
||||
|
||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);
|
||||
|
||||
int packedId = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
|
||||
int textureId = UnpackTextureId(packedId);
|
||||
UpdateCachedBuffer(stageIndex, ref cachedTextureBufferIndex, ref cachedSamplerBufferIndex, ref cachedTextureBuffer, ref cachedSamplerBuffer, textureBufferIndex, samplerBufferIndex);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||
@ -494,14 +682,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
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);
|
||||
}
|
||||
|
||||
_imageState[stageIndex][index].Texture = hostTexture;
|
||||
state.Texture = hostTexture;
|
||||
state.ScaleIndex = scaleIndex;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
Format format = bindingInfo.Format;
|
||||
|
||||
@ -512,8 +702,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
|
||||
}
|
||||
|
||||
state.CachedTexture = texture;
|
||||
state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return specStateMatches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -537,7 +732,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(cbufSlot, bufferIndex);
|
||||
|
||||
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
||||
int textureId = UnpackTextureId(packedId);
|
||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||
|
||||
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="samplerBufferIndex">Index of the constant buffer holding the sampler handles</param>
|
||||
/// <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)
|
||||
{
|
||||
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
||||
@ -590,32 +786,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
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>
|
||||
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
||||
/// </summary>
|
||||
public void Rebind()
|
||||
{
|
||||
_rebind = true;
|
||||
Array.Clear(_textureState);
|
||||
Array.Clear(_imageState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -349,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <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="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="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>
|
||||
@ -357,6 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
MemoryManager memoryManager,
|
||||
RtDepthStencilState dsState,
|
||||
Size3D size,
|
||||
bool layered,
|
||||
int samplesInX,
|
||||
int samplesInY,
|
||||
Size sizeHint)
|
||||
@ -364,9 +366,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||
|
||||
Target target = (samplesInX | samplesInY) != 1
|
||||
? Target.Texture2DMultisample
|
||||
: Target.Texture2D;
|
||||
Target target;
|
||||
|
||||
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();
|
||||
|
||||
@ -547,7 +564,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
range.Value,
|
||||
sizeInfo.LayerSize,
|
||||
_context.Capabilities,
|
||||
flags.HasFlag(TextureSearchFlags.ForCopy),
|
||||
out int firstLayer,
|
||||
out int firstLevel);
|
||||
|
||||
@ -662,7 +678,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
overlap.Range,
|
||||
overlap.LayerSize,
|
||||
_context.Capabilities,
|
||||
false,
|
||||
out int firstLayer,
|
||||
out int firstLevel);
|
||||
|
||||
|
@ -2,7 +2,6 @@ using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
case Target.Texture2DMultisample:
|
||||
case Target.Texture2DMultisampleArray:
|
||||
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
|
||||
{
|
||||
return TextureViewCompatibility.CopyOnly;
|
||||
}
|
||||
|
||||
result = rhs.Target == Target.Texture2DMultisample ||
|
||||
rhs.Target == Target.Texture2DMultisampleArray;
|
||||
break;
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
@ -10,9 +11,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
class TextureManager : IDisposable
|
||||
{
|
||||
private readonly GpuContext _context;
|
||||
private readonly GpuChannel _channel;
|
||||
|
||||
private readonly TextureBindingsManager _cpBindingsManager;
|
||||
private readonly TextureBindingsManager _gpBindingsManager;
|
||||
private readonly TexturePoolCache _texturePoolCache;
|
||||
|
||||
private readonly Texture[] _rtColors;
|
||||
private readonly ITexture[] _rtHostColors;
|
||||
@ -35,6 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
public TextureManager(GpuContext context, GpuChannel channel)
|
||||
{
|
||||
_context = context;
|
||||
_channel = channel;
|
||||
|
||||
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
|
||||
|
||||
@ -43,6 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true);
|
||||
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false);
|
||||
_texturePoolCache = texturePoolCache;
|
||||
|
||||
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||
@ -99,6 +104,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_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>
|
||||
/// Sets the texture constant buffer index on the graphics pipeline.
|
||||
/// </summary>
|
||||
@ -108,6 +123,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_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>
|
||||
/// Sets the current sampler pool on the compute pipeline.
|
||||
/// </summary>
|
||||
@ -335,25 +360,48 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <summary>
|
||||
/// Commits bindings on the compute pipeline.
|
||||
/// </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,
|
||||
// we must rebind everything.
|
||||
// Since compute work happens less often, we always do that
|
||||
// before and after the compute dispatch.
|
||||
_cpBindingsManager.Rebind();
|
||||
_cpBindingsManager.CommitBindings();
|
||||
bool result = _cpBindingsManager.CommitBindings(specState);
|
||||
_gpBindingsManager.Rebind();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits bindings on the graphics pipeline.
|
||||
/// </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();
|
||||
|
||||
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>
|
||||
|
@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
||||
private TextureDescriptor _defaultDescriptor;
|
||||
|
||||
/// <summary>
|
||||
/// Intrusive linked list node used on the texture pool cache.
|
||||
@ -32,6 +33,62 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_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>
|
||||
/// Gets the texture with the given ID.
|
||||
/// </summary>
|
||||
@ -51,56 +108,49 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
SynchronizeMemory();
|
||||
}
|
||||
|
||||
Texture texture = Items[id];
|
||||
|
||||
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();
|
||||
}
|
||||
GetInternal(id, out Texture 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>
|
||||
/// Forcibly remove a texture from this pool's items.
|
||||
/// 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="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
|
||||
/// <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 levels = descriptor.UnpackLevels();
|
||||
|
@ -378,6 +378,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
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>
|
||||
/// Ensures that the compute engine bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
|
@ -35,6 +35,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
HostProgram = hostProgram;
|
||||
SpecializationState = specializationState;
|
||||
Shaders = shaders;
|
||||
|
||||
SpecializationState.Prepare(shaders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -418,7 +418,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
|
||||
{
|
||||
return cpShader.SpecializationState.MatchesCompute(channel, poolState);
|
||||
return cpShader.SpecializationState.MatchesCompute(channel, poolState, true);
|
||||
}
|
||||
|
||||
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>
|
||||
|
@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState))
|
||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true))
|
||||
{
|
||||
program = entry;
|
||||
return true;
|
||||
@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
if (entry.SpecializationState.MatchesCompute(channel, poolState))
|
||||
if (entry.SpecializationState.MatchesCompute(channel, poolState, true))
|
||||
{
|
||||
program = entry;
|
||||
return true;
|
||||
|
@ -1,9 +1,14 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
@ -158,6 +163,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
|
||||
private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
|
||||
private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures;
|
||||
private Box<TextureSpecializationState>[][] _textureByBinding;
|
||||
private Box<TextureSpecializationState>[][] _imageByBinding;
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// Indicates that the shader accesses the early Z force state.
|
||||
/// </summary>
|
||||
@ -396,15 +446,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="poolState">Texture pool 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>
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Matches(channel, poolState, isCompute: false);
|
||||
return Matches(channel, poolState, checkTextures, isCompute: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -412,10 +463,64 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
/// <param name="channel">GPU channel</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>
|
||||
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>
|
||||
@ -423,9 +528,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
/// <param name="channel">GPU channel</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>
|
||||
/// <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;
|
||||
|
||||
@ -445,55 +551,60 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
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;
|
||||
ulong samplerCbAddress;
|
||||
|
||||
if (isCompute)
|
||||
foreach (var kv in _allTextures)
|
||||
{
|
||||
textureCbAddress = channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex);
|
||||
samplerCbAddress = channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
textureCbAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(textureKey.StageIndex, textureBufferIndex);
|
||||
samplerCbAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(textureKey.StageIndex, samplerBufferIndex);
|
||||
}
|
||||
TextureKey textureKey = kv.Key;
|
||||
|
||||
if (!channel.MemoryManager.Physical.IsMapped(textureCbAddress) || !channel.MemoryManager.Physical.IsMapped(samplerCbAddress))
|
||||
{
|
||||
continue;
|
||||
(int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(textureKey.CbufSlot, poolState.TextureBufferIndex);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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) &&
|
||||
specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized())
|
||||
{
|
||||
@ -504,6 +615,34 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
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>
|
||||
/// Reads shader specialization state that has been serialized.
|
||||
/// </summary>
|
||||
|
@ -9,10 +9,13 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
class Framebuffer : IDisposable
|
||||
{
|
||||
public int Handle { get; private set; }
|
||||
private int _clearFbHandle;
|
||||
private bool _clearFbInitialized;
|
||||
|
||||
private FramebufferAttachment _lastDsAttachment;
|
||||
|
||||
private readonly TextureView[] _colors;
|
||||
private TextureView _depthStencil;
|
||||
|
||||
private int _colorsCount;
|
||||
private bool _dualSourceBlend;
|
||||
@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
public Framebuffer()
|
||||
{
|
||||
Handle = GL.GenFramebuffer();
|
||||
_clearFbHandle = GL.GenFramebuffer();
|
||||
|
||||
_colors = new TextureView[8];
|
||||
}
|
||||
@ -55,20 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
if (depthStencil != null)
|
||||
{
|
||||
FramebufferAttachment attachment;
|
||||
|
||||
if (IsPackedDepthStencilFormat(depthStencil.Format))
|
||||
{
|
||||
attachment = FramebufferAttachment.DepthStencilAttachment;
|
||||
}
|
||||
else if (IsDepthOnlyFormat(depthStencil.Format))
|
||||
{
|
||||
attachment = FramebufferAttachment.DepthAttachment;
|
||||
}
|
||||
else
|
||||
{
|
||||
attachment = FramebufferAttachment.StencilAttachment;
|
||||
}
|
||||
FramebufferAttachment attachment = GetAttachment(depthStencil.Format);
|
||||
|
||||
GL.FramebufferTexture(
|
||||
FramebufferTarget.Framebuffer,
|
||||
@ -82,6 +73,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
_lastDsAttachment = 0;
|
||||
}
|
||||
|
||||
_depthStencil = depthStencil;
|
||||
}
|
||||
|
||||
public void SetDualSourceBlend(bool enable)
|
||||
@ -124,6 +117,22 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
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)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint ||
|
||||
@ -136,6 +145,78 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
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()
|
||||
{
|
||||
if (Handle != 0)
|
||||
@ -144,6 +225,13 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
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;
|
||||
|
||||
public IntermmediatePool IntermmediatePool { get; }
|
||||
|
||||
private int _srcFramebuffer;
|
||||
private int _dstFramebuffer;
|
||||
|
||||
@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
public TextureCopy(Renderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
IntermmediatePool = new IntermmediatePool(renderer);
|
||||
}
|
||||
|
||||
public void Copy(
|
||||
@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
TextureView dst,
|
||||
Extents2D srcRegion,
|
||||
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;
|
||||
|
||||
@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
|
||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
|
||||
|
||||
int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
|
||||
int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers());
|
||||
if (srcLevel != 0)
|
||||
{
|
||||
srcRegion = srcRegion.Reduce(srcLevel);
|
||||
}
|
||||
|
||||
if (dstLevel != 0)
|
||||
{
|
||||
dstRegion = dstRegion.Reduce(dstLevel);
|
||||
}
|
||||
|
||||
for (int level = 0; level < levels; level++)
|
||||
{
|
||||
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.DrawFramebuffer, dst.Format, dst.Handle, level, layer);
|
||||
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer);
|
||||
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level);
|
||||
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level);
|
||||
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level);
|
||||
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level);
|
||||
}
|
||||
|
||||
ClearBufferMask mask = GetMask(src.Format);
|
||||
@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
|
||||
_copyPboHandle = 0;
|
||||
}
|
||||
|
||||
IntermmediatePool.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
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(
|
||||
index,
|
||||
@ -119,14 +119,18 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
(componentMask & 4) != 0,
|
||||
(componentMask & 8) != 0);
|
||||
|
||||
_framebuffer.AttachColorLayerForClear(index, layer);
|
||||
|
||||
float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
|
||||
|
||||
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
|
||||
|
||||
_framebuffer.DetachColorLayerForClear(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 =
|
||||
stencilMask != 0 &&
|
||||
@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
GL.DepthMask(depthMask);
|
||||
}
|
||||
|
||||
_framebuffer.AttachDepthStencilLayerForClear(layer);
|
||||
|
||||
if (depthMask && stencilMask != 0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
_framebuffer.DetachDepthStencilLayerForClear();
|
||||
|
||||
if (stencilMaskChanged)
|
||||
{
|
||||
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
|
||||
@ -597,6 +605,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
GL.EndTransformFeedback();
|
||||
}
|
||||
|
||||
GL.ClipControl(ClipOrigin.UpperLeft, ClipDepthMode.NegativeOneToOne);
|
||||
|
||||
_drawTexture.Draw(
|
||||
view,
|
||||
samp,
|
||||
@ -627,6 +637,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
GL.BeginTransformFeedback(_tfTopology);
|
||||
}
|
||||
|
||||
RestoreClipControl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
@ -50,5 +51,63 @@ namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
@ -62,9 +67,9 @@ namespace Ryujinx.Graphics.Vic
|
||||
Pixel* ip = srcPtr;
|
||||
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> 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.Vic.Image;
|
||||
using Ryujinx.Graphics.Vic.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
@ -280,13 +280,6 @@ namespace Ryujinx.HLE.HOS
|
||||
return;
|
||||
}
|
||||
|
||||
if (mainNca == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "Unable to load NSP: Could not find Main NCA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mainNca != null)
|
||||
{
|
||||
_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
|
||||
LoadExeFs(nsp);
|
||||
LoadExeFs(nsp, null, isHomebrew: true);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
@ -661,7 +654,7 @@ namespace Ryujinx.HLE.HOS
|
||||
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
|
||||
|
||||
// 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();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);
|
||||
|
@ -1,5 +1,4 @@
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
@ -11,12 +10,12 @@ namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
private readonly ulong _pid;
|
||||
private readonly GpuContext _gpuContext;
|
||||
private readonly CpuContext _cpuContext;
|
||||
private readonly ICpuContext _cpuContext;
|
||||
private T _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)
|
||||
{
|
||||
@ -27,11 +26,16 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
_pid = pid;
|
||||
_gpuContext = gpuContext;
|
||||
_cpuContext = new CpuContext(memoryManager, for64Bit);
|
||||
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit);
|
||||
_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);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
@ -10,10 +11,12 @@ namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
class ArmProcessContextFactory : IProcessContextFactory
|
||||
{
|
||||
private readonly ICpuEngine _cpuEngine;
|
||||
private readonly GpuContext _gpu;
|
||||
|
||||
public ArmProcessContextFactory(GpuContext gpu)
|
||||
public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu)
|
||||
{
|
||||
_cpuEngine = cpuEngine;
|
||||
_gpu = gpu;
|
||||
}
|
||||
|
||||
@ -29,12 +32,14 @@ namespace Ryujinx.HLE.HOS
|
||||
switch (mode)
|
||||
{
|
||||
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.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:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
@ -10,6 +10,8 @@ using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Audio.Output;
|
||||
using Ryujinx.Audio.Renderer.Device;
|
||||
using Ryujinx.Audio.Renderer.Server;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
@ -57,6 +59,9 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
internal Switch Device { get; private set; }
|
||||
|
||||
internal ITickSource TickSource { get; }
|
||||
internal ICpuEngine CpuEngine { get; }
|
||||
|
||||
internal SurfaceFlinger SurfaceFlinger { get; private set; }
|
||||
internal AudioManager AudioManager { get; private set; }
|
||||
internal AudioOutputManager AudioOutputManager { get; private set; }
|
||||
@ -121,7 +126,11 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
public Horizon(Switch device)
|
||||
{
|
||||
TickSource = new TickSource(KernelConstants.CounterFrequency);
|
||||
CpuEngine = new JitEngine(TickSource);
|
||||
|
||||
KernelContext = new KernelContext(
|
||||
TickSource,
|
||||
device,
|
||||
device.Memory,
|
||||
device.Configuration.MemoryConfiguration.ToKernelMemorySize(),
|
||||
@ -215,40 +224,40 @@ namespace Ryujinx.HLE.HOS
|
||||
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
|
||||
|
||||
// First init the standard steady clock
|
||||
TimeServiceManager.Instance.SetupStandardSteadyClock(null, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
|
||||
TimeServiceManager.Instance.SetupStandardLocalSystemClock(null, new SystemClockContext(), systemTime.ToSeconds());
|
||||
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
|
||||
TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
|
||||
|
||||
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
|
||||
{
|
||||
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.
|
||||
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(null, out SystemClockContext localSytemClockContext);
|
||||
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
|
||||
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
|
||||
|
||||
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
||||
|
||||
DatabaseImpl.Instance.InitializeDatabase(LibHacHorizonManager.SdbClient);
|
||||
DatabaseImpl.Instance.InitializeDatabase(TickSource, LibHacHorizonManager.SdbClient);
|
||||
|
||||
HostSyncpoint = new NvHostSyncpt(device);
|
||||
|
||||
SurfaceFlinger = new SurfaceFlinger(device);
|
||||
|
||||
InitializeAudioRenderer();
|
||||
InitializeAudioRenderer(TickSource);
|
||||
InitializeServices();
|
||||
}
|
||||
|
||||
private void InitializeAudioRenderer()
|
||||
private void InitializeAudioRenderer(ITickSource tickSource)
|
||||
{
|
||||
AudioManager = new AudioManager();
|
||||
AudioOutputManager = new AudioOutputManager();
|
||||
AudioInputManager = new AudioInputManager();
|
||||
AudioRendererManager = new AudioRendererManager();
|
||||
AudioRendererManager = new AudioRendererManager(tickSource);
|
||||
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
|
||||
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
|
||||
|
||||
@ -492,12 +501,12 @@ namespace Ryujinx.HLE.HOS
|
||||
if (pause && !IsPaused)
|
||||
{
|
||||
Device.AudioDeviceDriver.GetPauseEvent().Reset();
|
||||
ARMeilleure.State.ExecutionContext.SuspendCounter();
|
||||
TickSource.Suspend();
|
||||
}
|
||||
else if (!pause && IsPaused)
|
||||
{
|
||||
Device.AudioDeviceDriver.GetPauseEvent().Set();
|
||||
ARMeilleure.State.ExecutionContext.ResumeCounter();
|
||||
TickSource.Resume();
|
||||
}
|
||||
}
|
||||
IsPaused = pause;
|
||||
|
@ -12,5 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||
public const ulong UserSlabHeapItemSize = KPageTableBase.PageSize;
|
||||
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.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
|
||||
@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||
|
||||
public Switch Device { get; }
|
||||
public MemoryBlock Memory { get; }
|
||||
public ITickSource TickSource { get; }
|
||||
public Syscall Syscall { get; }
|
||||
public SyscallHandler SyscallHandler { get; }
|
||||
|
||||
@ -52,11 +54,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||
private ulong _threadUid;
|
||||
|
||||
public KernelContext(
|
||||
ITickSource tickSource,
|
||||
Switch device,
|
||||
MemoryBlock memory,
|
||||
MemorySize memorySize,
|
||||
MemoryArrange memoryArrange)
|
||||
{
|
||||
TickSource = tickSource;
|
||||
Device = device;
|
||||
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 System.Diagnostics;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
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 EndAddr { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
|
||||
private int _blockOrdersCount;
|
||||
|
||||
private readonly KMemoryRegionBlock[] _blocks;
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
public ulong EndAddr => Address + Size;
|
||||
|
||||
private readonly ushort[] _pageReferenceCounts;
|
||||
|
||||
public KMemoryRegionManager(ulong address, ulong size, ulong endAddr)
|
||||
{
|
||||
_blocks = new KMemoryRegionBlock[BlockOrders.Length];
|
||||
|
||||
Address = address;
|
||||
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];
|
||||
}
|
||||
}
|
||||
Size = size;
|
||||
|
||||
_pageReferenceCounts = new ushort[size / KPageTableBase.PageSize];
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
FreePages(address, size / KPageTableBase.PageSize);
|
||||
}
|
||||
_pageHeap = new KPageHeap(address, size);
|
||||
_pageHeap.Free(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)
|
||||
{
|
||||
@ -112,9 +52,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
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)
|
||||
{
|
||||
@ -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();
|
||||
|
||||
if (_blockOrdersCount > 0)
|
||||
{
|
||||
if (GetFreePagesImpl() < pagesCount)
|
||||
{
|
||||
return KernelResult.OutOfMemory;
|
||||
}
|
||||
}
|
||||
else if (pagesCount != 0)
|
||||
int heapIndex = KPageHeap.GetBlockIndex(pagesCount);
|
||||
|
||||
if (heapIndex < 0)
|
||||
{
|
||||
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;
|
||||
|
||||
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)
|
||||
while (pagesCount >= pagesPerAlloc)
|
||||
{
|
||||
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,
|
||||
// just give up and try with the next one.
|
||||
if (address == 0)
|
||||
if (allocatedBlock == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Add new allocated page(s) to the pages list.
|
||||
// If an error occurs, then free all allocated pages and fail.
|
||||
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
||||
KernelResult result = pageList.AddRange(allocatedBlock, pagesPerAlloc);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
FreePages(address, blockPagesCount);
|
||||
|
||||
foreach (KPageNode pageNode in pageList)
|
||||
{
|
||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
||||
}
|
||||
FreePages(pageList);
|
||||
_pageHeap.Free(allocatedBlock, pagesPerAlloc);
|
||||
|
||||
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.
|
||||
foreach (KPageNode pageNode in pageList)
|
||||
{
|
||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
||||
}
|
||||
|
||||
pageList = null;
|
||||
|
||||
return KernelResult.OutOfMemory;
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int blockIndex = 0;
|
||||
ulong allocatedPages = KPageHeap.GetBlockPagesCount(heapIndex);
|
||||
|
||||
while ((1UL << _blocks[blockIndex].Order) / KPageTableBase.PageSize < pagesCount)
|
||||
if (allocatedPages > pagesCount)
|
||||
{
|
||||
if (++blockIndex >= _blocks.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
_pageHeap.Free(allocatedBlock + pagesCount * KPageTableBase.PageSize, allocatedPages - pagesCount);
|
||||
}
|
||||
|
||||
ulong tightestFitBlockSize = 1UL << _blocks[blockIndex].Order;
|
||||
|
||||
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;
|
||||
return allocatedBlock;
|
||||
}
|
||||
|
||||
private ulong AllocatePagesForOrder(int blockIndex, bool backwards, ulong bestFitBlockSize)
|
||||
public void FreePage(ulong address)
|
||||
{
|
||||
ulong address = 0;
|
||||
|
||||
KMemoryRegionBlock block = null;
|
||||
|
||||
for (int currBlockIndex = blockIndex;
|
||||
currBlockIndex < _blockOrdersCount && address == 0;
|
||||
currBlockIndex++)
|
||||
lock (_pageHeap)
|
||||
{
|
||||
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 + 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);
|
||||
_pageHeap.Free(address, 1);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
int blockIndex = _blockOrdersCount - 1;
|
||||
|
||||
ulong addressRounded = 0;
|
||||
ulong endAddrTruncated = 0;
|
||||
|
||||
for (; blockIndex >= 0; blockIndex--)
|
||||
foreach (KPageNode pageNode in pageList)
|
||||
{
|
||||
KMemoryRegionBlock allocInfo = _blocks[blockIndex];
|
||||
|
||||
int blockSize = 1 << allocInfo.Order;
|
||||
|
||||
addressRounded = BitUtils.AlignUp (address, blockSize);
|
||||
endAddrTruncated = BitUtils.AlignDown(endAddr, blockSize);
|
||||
|
||||
if (addressRounded < endAddrTruncated)
|
||||
{
|
||||
break;
|
||||
}
|
||||
_pageHeap.Free(pageNode.Address, pageNode.PagesCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeRegion(ulong currAddress)
|
||||
{
|
||||
for (int currBlockIndex = blockIndex;
|
||||
currBlockIndex < _blockOrdersCount && currAddress != 0;
|
||||
currBlockIndex++)
|
||||
{
|
||||
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 void FreePages(ulong address, ulong pagesCount)
|
||||
{
|
||||
lock (_pageHeap)
|
||||
{
|
||||
_pageHeap.Free(address, pagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
|
||||
KernelResult result = region.AllocatePages(pagesCount, _aslrDisabled, out KPageList pageList);
|
||||
KernelResult result = region.AllocatePages(out KPageList pageList, pagesCount);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
@ -712,7 +712,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
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));
|
||||
|
||||
@ -1276,7 +1276,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
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));
|
||||
|
||||
|
@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
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))
|
||||
{
|
||||
return $"0x{v:x16}";
|
||||
|
@ -1,4 +1,4 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
|
||||
@ -8,7 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
@ -736,22 +735,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
ulong argsPtr,
|
||||
ulong stackTop,
|
||||
int priority,
|
||||
int cpuCore)
|
||||
int cpuCore,
|
||||
ThreadStart customThreadStart = null)
|
||||
{
|
||||
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;
|
||||
context.SupervisorCall += KernelContext.SyscallHandler.SvcCall;
|
||||
context.Undefined += UndefinedInstructionHandler;
|
||||
return Context?.CreateExecutionContext(new ExceptionCallbacks(
|
||||
InterruptHandler,
|
||||
null,
|
||||
KernelContext.SyscallHandler.SvcCall,
|
||||
UndefinedInstructionHandler));
|
||||
}
|
||||
|
||||
private void InterruptHandler(object sender, EventArgs e)
|
||||
private void InterruptHandler(IExecutionContext context)
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
@ -1093,12 +1095,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
|
||||
private void UndefinedInstructionHandler(IExecutionContext context, ulong address, int opCode)
|
||||
{
|
||||
KernelStatic.GetCurrentThread().PrintGuestStackTrace();
|
||||
KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout();
|
||||
|
||||
throw new UndefinedInstructionException(e.Address, e.OpCode);
|
||||
throw new UndefinedInstructionException(address, opCode);
|
||||
}
|
||||
|
||||
protected override void Destroy() => Context.Dispose();
|
||||
|
@ -1,4 +1,4 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
|
||||
@ -13,7 +13,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
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();
|
||||
}
|
||||
|
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
|
||||
{
|
||||
[SvcImpl]
|
||||
class Syscall
|
||||
{
|
||||
private readonly KernelContext _context;
|
||||
@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
// Process
|
||||
|
||||
[Svc(0x24)]
|
||||
public KernelResult GetProcessId(out ulong pid, int handle)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -167,9 +169,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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
|
||||
|
||||
public KernelResult ConnectToNamedPort(out int handle, ulong namePtr)
|
||||
[Svc(0x1f)]
|
||||
public KernelResult ConnectToNamedPort(out int handle, [PointerSized] ulong namePtr)
|
||||
{
|
||||
handle = 0;
|
||||
|
||||
@ -222,6 +233,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x21)]
|
||||
public KernelResult SendSyncRequest(int handle)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -236,7 +248,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -283,7 +299,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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;
|
||||
|
||||
@ -353,11 +374,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x40)]
|
||||
public KernelResult CreateSession(
|
||||
out int serverSessionHandle,
|
||||
out int clientSessionHandle,
|
||||
bool isLight,
|
||||
ulong namePtr)
|
||||
[PointerSized] ulong namePtr)
|
||||
{
|
||||
serverSessionHandle = 0;
|
||||
clientSessionHandle = 0;
|
||||
@ -419,6 +441,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x41)]
|
||||
public KernelResult AcceptSession(out int sessionHandle, int portHandle)
|
||||
{
|
||||
sessionHandle = 0;
|
||||
@ -470,9 +493,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x43)]
|
||||
public KernelResult ReplyAndReceive(
|
||||
out int handleIndex,
|
||||
ulong handlesPtr,
|
||||
[PointerSized] ulong handlesPtr,
|
||||
int handlesCount,
|
||||
int replyTargetHandle,
|
||||
long timeout)
|
||||
@ -575,11 +599,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x44)]
|
||||
public KernelResult ReplyAndReceiveWithUserBuffer(
|
||||
out int handleIndex,
|
||||
ulong handlesPtr,
|
||||
ulong messagePtr,
|
||||
ulong messageSize,
|
||||
[PointerSized] ulong messagePtr,
|
||||
[PointerSized] ulong messageSize,
|
||||
[PointerSized] ulong handlesPtr,
|
||||
int handlesCount,
|
||||
int replyTargetHandle,
|
||||
long timeout)
|
||||
@ -679,12 +704,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x70)]
|
||||
public KernelResult CreatePort(
|
||||
out int serverPortHandle,
|
||||
out int clientPortHandle,
|
||||
int maxSessions,
|
||||
bool isLight,
|
||||
ulong namePtr)
|
||||
[PointerSized] ulong namePtr)
|
||||
{
|
||||
serverPortHandle = clientPortHandle = 0;
|
||||
|
||||
@ -714,7 +740,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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;
|
||||
|
||||
@ -766,6 +793,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x72)]
|
||||
public KernelResult ConnectToPort(out int clientSessionHandle, int clientPortHandle)
|
||||
{
|
||||
clientSessionHandle = 0;
|
||||
@ -819,7 +847,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
// 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)
|
||||
{
|
||||
@ -833,7 +862,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -865,9 +895,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission);
|
||||
}
|
||||
|
||||
[Svc(3)]
|
||||
public KernelResult SetMemoryAttribute(
|
||||
ulong address,
|
||||
ulong size,
|
||||
[PointerSized] ulong address,
|
||||
[PointerSized] ulong size,
|
||||
MemoryAttribute attributeMask,
|
||||
MemoryAttribute attributeValue)
|
||||
{
|
||||
@ -905,7 +936,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -941,7 +973,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -977,7 +1010,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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);
|
||||
|
||||
@ -1011,7 +1045,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1057,7 +1092,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1097,7 +1133,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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;
|
||||
|
||||
@ -1160,7 +1197,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1206,7 +1244,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1246,7 +1285,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
currentProcess);
|
||||
}
|
||||
|
||||
public KernelResult MapPhysicalMemory(ulong address, ulong size)
|
||||
[Svc(0x2c)]
|
||||
public KernelResult MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
|
||||
{
|
||||
if (!PageAligned(address))
|
||||
{
|
||||
@ -1281,7 +1321,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1316,7 +1357,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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;
|
||||
|
||||
@ -1356,7 +1398,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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();
|
||||
|
||||
@ -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))
|
||||
{
|
||||
@ -1465,7 +1518,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1517,7 +1575,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -1558,6 +1621,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x77)]
|
||||
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||
{
|
||||
if (!PageAligned(dst) || !PageAligned(src))
|
||||
@ -1595,6 +1659,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
|
||||
}
|
||||
|
||||
[Svc(0x78)]
|
||||
public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
|
||||
{
|
||||
if (!PageAligned(dst) || !PageAligned(src))
|
||||
@ -1639,6 +1704,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
// System
|
||||
|
||||
[Svc(0x7b)]
|
||||
public KernelResult TerminateProcess(int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -1668,11 +1734,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(7)]
|
||||
public void ExitProcess()
|
||||
{
|
||||
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
|
||||
}
|
||||
|
||||
[Svc(0x11)]
|
||||
public KernelResult SignalEvent(int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -1695,6 +1763,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x12)]
|
||||
public KernelResult ClearEvent(int handle)
|
||||
{
|
||||
KernelResult result;
|
||||
@ -1717,6 +1786,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x16)]
|
||||
public KernelResult CloseHandle(int handle)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -1724,6 +1794,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
[Svc(0x17)]
|
||||
public KernelResult ResetSignal(int handle)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -1753,11 +1824,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x1e)]
|
||||
public ulong GetSystemTick()
|
||||
{
|
||||
return KernelStatic.GetCurrentThread().Context.CntpctEl0;
|
||||
return _context.TickSource.Counter;
|
||||
}
|
||||
|
||||
[Svc(0x26)]
|
||||
public void Break(ulong reason)
|
||||
{
|
||||
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();
|
||||
|
||||
@ -1793,6 +1867,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
Logger.Warning?.Print(LogClass.KernelSvc, str);
|
||||
}
|
||||
|
||||
[Svc(0x29)]
|
||||
public KernelResult GetInfo(out ulong value, InfoType id, int handle, long subId)
|
||||
{
|
||||
value = 0;
|
||||
@ -2038,6 +2113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x45)]
|
||||
public KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
|
||||
{
|
||||
KEvent Event = new KEvent(_context);
|
||||
@ -2063,7 +2139,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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;
|
||||
|
||||
@ -2112,6 +2189,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x6f)]
|
||||
public KernelResult GetSystemInfo(out long value, uint id, int handle, long subId)
|
||||
{
|
||||
value = 0;
|
||||
@ -2168,6 +2246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x30)]
|
||||
public KernelResult GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource)
|
||||
{
|
||||
limitValue = 0;
|
||||
@ -2189,6 +2268,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x31)]
|
||||
public KernelResult GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource)
|
||||
{
|
||||
limitValue = 0;
|
||||
@ -2210,6 +2290,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x37)]
|
||||
public KernelResult GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource)
|
||||
{
|
||||
peak = 0;
|
||||
@ -2231,6 +2312,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0x7d)]
|
||||
public KernelResult CreateResourceLimit(out int handle)
|
||||
{
|
||||
KResourceLimit limit = new KResourceLimit(_context);
|
||||
@ -2240,6 +2322,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return process.HandleTable.GenerateHandle(limit, out handle);
|
||||
}
|
||||
|
||||
[Svc(0x7e)]
|
||||
public KernelResult SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue)
|
||||
{
|
||||
if (resource >= LimitableResource.Count)
|
||||
@ -2259,13 +2342,26 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
// 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(
|
||||
out int handle,
|
||||
ulong entrypoint,
|
||||
ulong argsPtr,
|
||||
ulong stackTop,
|
||||
int priority,
|
||||
int cpuCore)
|
||||
int cpuCore,
|
||||
ThreadStart customThreadStart)
|
||||
{
|
||||
handle = 0;
|
||||
|
||||
@ -2302,7 +2398,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
argsPtr,
|
||||
stackTop,
|
||||
priority,
|
||||
cpuCore);
|
||||
cpuCore,
|
||||
customThreadStart);
|
||||
|
||||
if (result == KernelResult.Success)
|
||||
{
|
||||
@ -2320,6 +2417,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(9)]
|
||||
public KernelResult StartThread(int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2347,6 +2445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
}
|
||||
|
||||
[Svc(0xa)]
|
||||
public void ExitThread()
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
@ -2354,6 +2453,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
currentThread.Exit();
|
||||
}
|
||||
|
||||
[Svc(0xb)]
|
||||
public void SleepThread(long timeout)
|
||||
{
|
||||
if (timeout < 1)
|
||||
@ -2371,6 +2471,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
}
|
||||
|
||||
[Svc(0xc)]
|
||||
public KernelResult GetThreadPriority(out int priority, int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2391,6 +2492,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
}
|
||||
|
||||
[Svc(0xd)]
|
||||
public KernelResult SetThreadPriority(int handle, int priority)
|
||||
{
|
||||
// TODO: NPDM check.
|
||||
@ -2409,6 +2511,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
[Svc(0xe)]
|
||||
public KernelResult GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle)
|
||||
{
|
||||
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)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
@ -2479,11 +2583,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
|
||||
}
|
||||
|
||||
[Svc(0x10)]
|
||||
public int GetCurrentProcessorNumber()
|
||||
{
|
||||
return KernelStatic.GetCurrentThread().CurrentCore;
|
||||
}
|
||||
|
||||
[Svc(0x25)]
|
||||
public KernelResult GetThreadId(out ulong threadUid, int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2504,6 +2610,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
}
|
||||
}
|
||||
|
||||
[Svc(0x32)]
|
||||
public KernelResult SetThreadActivity(int handle, bool pause)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2528,7 +2635,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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();
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
@ -2564,7 +2672,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
||||
// 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;
|
||||
|
||||
@ -2653,6 +2762,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return result;
|
||||
}
|
||||
|
||||
[Svc(0x19)]
|
||||
public KernelResult CancelSynchronization(int handle)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
@ -2669,7 +2779,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -2686,7 +2797,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
|
||||
}
|
||||
|
||||
public KernelResult ArbitrateUnlock(ulong mutexAddress)
|
||||
[Svc(0x1b)]
|
||||
public KernelResult ArbitrateUnlock([PointerSized] ulong mutexAddress)
|
||||
{
|
||||
if (IsPointingInsideKernel(mutexAddress))
|
||||
{
|
||||
@ -2703,9 +2815,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
|
||||
}
|
||||
|
||||
[Svc(0x1c)]
|
||||
public KernelResult WaitProcessWideKeyAtomic(
|
||||
ulong mutexAddress,
|
||||
ulong condVarAddress,
|
||||
[PointerSized] ulong mutexAddress,
|
||||
[PointerSized] ulong condVarAddress,
|
||||
int handle,
|
||||
long timeout)
|
||||
{
|
||||
@ -2733,7 +2846,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
timeout);
|
||||
}
|
||||
|
||||
public KernelResult SignalProcessWideKey(ulong address, int count)
|
||||
[Svc(0x1d)]
|
||||
public KernelResult SignalProcessWideKey([PointerSized] ulong address, int count)
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
|
||||
@ -2742,7 +2856,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
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))
|
||||
{
|
||||
@ -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))
|
||||
{
|
||||
@ -2799,6 +2915,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
};
|
||||
}
|
||||
|
||||
[Svc(0x36)]
|
||||
public KernelResult SynchronizePreemptionState()
|
||||
{
|
||||
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
|
||||
@ -2806,12 +2923,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private bool IsPointingInsideKernel(ulong address)
|
||||
private static bool IsPointingInsideKernel(ulong address)
|
||||
{
|
||||
return (address + 0x1000000000) < 0xffffff000;
|
||||
}
|
||||
|
||||
private bool IsAddressNotWordAligned(ulong address)
|
||||
private static bool IsAddressNotWordAligned(ulong address)
|
||||
{
|
||||
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 System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
{
|
||||
partial class SyscallHandler
|
||||
{
|
||||
private readonly KernelContext _context;
|
||||
private readonly Syscall32 _syscall32;
|
||||
private readonly Syscall64 _syscall64;
|
||||
|
||||
public SyscallHandler(KernelContext 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();
|
||||
|
||||
@ -34,29 +29,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
_context.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
ExecutionContext context = (ExecutionContext)sender;
|
||||
|
||||
if (context.IsAarch32)
|
||||
{
|
||||
var svcFunc = SyscallTable.SvcTable32[e.Id];
|
||||
|
||||
if (svcFunc == null)
|
||||
{
|
||||
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
|
||||
}
|
||||
|
||||
svcFunc(_syscall32, context);
|
||||
SyscallDispatch.Dispatch32(_context.Syscall, context, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
var svcFunc = SyscallTable.SvcTable64[e.Id];
|
||||
|
||||
if (svcFunc == null)
|
||||
{
|
||||
throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
|
||||
}
|
||||
|
||||
svcFunc(_syscall64, context);
|
||||
SyscallDispatch.Dispatch64(_context.Syscall, context, id);
|
||||
}
|
||||
|
||||
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 ARMeilleure.State.ExecutionContext Context { get; private set; }
|
||||
public IExecutionContext Context { 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 long LastPc { get; set; }
|
||||
|
||||
private object ActivityOperationLock = new object();
|
||||
private object _activityOperationLock;
|
||||
|
||||
public KThread(KernelContext context) : base(context)
|
||||
{
|
||||
@ -128,6 +126,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
_mutexWaiters = new LinkedList<KThread>();
|
||||
_pinnedWaiters = new LinkedList<KThread>();
|
||||
|
||||
_activityOperationLock = new object();
|
||||
}
|
||||
|
||||
public KernelResult Initialize(
|
||||
@ -192,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
HostThread = new Thread(ThreadStart);
|
||||
|
||||
Context = CpuContext.CreateExecutionContext();
|
||||
Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext();
|
||||
|
||||
Context.IsAarch32 = !is64Bits;
|
||||
|
||||
@ -208,8 +208,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
Context.SetX(13, (uint)stackTop);
|
||||
}
|
||||
|
||||
Context.CntfrqEl0 = 19200000;
|
||||
Context.Tpidr = (long)_tlsAddress;
|
||||
Context.TpidrroEl0 = (long)_tlsAddress;
|
||||
|
||||
ThreadUid = KernelContext.NewThreadUid();
|
||||
|
||||
@ -221,7 +220,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
owner.SubscribeThreadEventHandlers(Context);
|
||||
owner.AddThread(this);
|
||||
|
||||
if (owner.IsPaused)
|
||||
@ -538,7 +536,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
public KernelResult SetActivity(bool pause)
|
||||
{
|
||||
lock (ActivityOperationLock)
|
||||
lock (_activityOperationLock)
|
||||
{
|
||||
KernelResult result = KernelResult.Success;
|
||||
|
||||
@ -634,7 +632,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
{
|
||||
context = default;
|
||||
|
||||
lock (ActivityOperationLock)
|
||||
lock (_activityOperationLock)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
@ -656,7 +654,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private static uint GetPsr(ARMeilleure.State.ExecutionContext context)
|
||||
private static uint GetPsr(IExecutionContext context)
|
||||
{
|
||||
return context.Pstate & 0xFF0FFE20;
|
||||
}
|
||||
@ -683,9 +681,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
context.Fp = Context.GetX(29);
|
||||
context.Lr = Context.GetX(30);
|
||||
context.Sp = Context.GetX(31);
|
||||
context.Pc = (ulong)LastPc;
|
||||
context.Pc = Context.Pc;
|
||||
context.Pstate = GetPsr(Context);
|
||||
context.Tpidr = (ulong)Context.Tpidr;
|
||||
context.Tpidr = (ulong)Context.TpidrroEl0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -699,9 +697,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
context.FpuRegisters[i] = Context.GetV(i);
|
||||
}
|
||||
|
||||
context.Pc = (uint)LastPc;
|
||||
context.Pc = (uint)Context.Pc;
|
||||
context.Pstate = GetPsr(Context);
|
||||
context.Tpidr = (uint)Context.Tpidr;
|
||||
context.Tpidr = (uint)Context.TpidrroEl0;
|
||||
}
|
||||
|
||||
context.Fpcr = (uint)Context.Fpcr;
|
||||
@ -743,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
public KernelResult SetCoreAndAffinityMask(int newCore, ulong newAffinityMask)
|
||||
{
|
||||
lock (ActivityOperationLock)
|
||||
lock (_activityOperationLock)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
|
@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
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)
|
||||
{
|
||||
@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
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(
|
||||
creationInfo,
|
||||
@ -264,7 +264,7 @@ namespace Ryujinx.HLE.HOS
|
||||
return false;
|
||||
}
|
||||
|
||||
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
|
||||
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
|
||||
|
||||
result = process.Initialize(
|
||||
creationInfo,
|
||||
|
@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
|
||||
}
|
||||
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;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user