Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
36f00985d3 | ||
|
748d87adcc | ||
|
0fd47ff490 | ||
|
f088c3d344 | ||
|
905a191e28 | ||
|
ab0491817e | ||
|
5de6ae426e | ||
|
69ced3a6e8 | ||
|
2e43d01d36 | ||
|
7373ec5792 | ||
|
de162a648b | ||
|
131baebe2a | ||
|
187372cbde | ||
|
022d495335 | ||
|
c1372ed775 | ||
|
a16682cfd3 | ||
|
7c53b69c30 | ||
|
33a4d7d1ba | ||
|
391e08dd27 | ||
|
b5cf8b8af9 | ||
|
55043c8afc | ||
|
5d73a9f5fc |
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -52,26 +52,22 @@ jobs:
|
|||||||
- uses: actions/setup-dotnet@v3
|
- uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 7.0.x
|
||||||
- name: Ensure NuGet Source
|
|
||||||
uses: fabriciomurta/ensure-nuget-source@v1
|
|
||||||
- name: Get git short hash
|
- name: Get git short hash
|
||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Clear
|
|
||||||
run: dotnet clean && dotnet nuget locals all --clear
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
|
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained true
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- name: Publish Ryujinx.Headless.SDL2
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained true
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
- name: Publish Ryujinx.Ava
|
- name: Publish Ryujinx.Ava
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -29,10 +29,6 @@ jobs:
|
|||||||
- uses: actions/setup-dotnet@v3
|
- uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 7.0.x
|
||||||
- name: Ensure NuGet Source
|
|
||||||
uses: fabriciomurta/ensure-nuget-source@v1
|
|
||||||
- name: Clear
|
|
||||||
run: dotnet clean && dotnet nuget locals all --clear
|
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
@@ -51,9 +47,9 @@ jobs:
|
|||||||
run: "mkdir release_output"
|
run: "mkdir release_output"
|
||||||
- name: Publish Windows
|
- name: Publish Windows
|
||||||
run: |
|
run: |
|
||||||
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/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||||
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_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 true
|
||||||
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
|
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 true
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
run: |
|
run: |
|
||||||
pushd publish_windows
|
pushd publish_windows
|
||||||
@@ -71,9 +67,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish Linux
|
- name: Publish Linux
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/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 linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_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 linux-x64 -o ./publish_linux_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 true
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_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 linux-x64 -o ./publish_linux_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 true
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
run: |
|
run: |
|
||||||
|
@@ -1,15 +1,11 @@
|
|||||||
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace ARMeilleure.Common
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
static class BitUtils
|
static class BitUtils
|
||||||
{
|
{
|
||||||
private static readonly sbyte[] HbsNibbleLut;
|
private static ReadOnlySpan<sbyte> HbsNibbleLut => new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
||||||
|
|
||||||
static BitUtils()
|
|
||||||
{
|
|
||||||
HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long FillWithOnes(int bits)
|
public static long FillWithOnes(int bits)
|
||||||
{
|
{
|
||||||
|
@@ -119,7 +119,7 @@
|
|||||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||||
"SettingsTabSystemHacks": "Hacks",
|
"SettingsTabSystemHacks": "Hacks",
|
||||||
"SettingsTabSystemHacksNote": " (may cause instability)",
|
"SettingsTabSystemHacksNote": " (may cause instability)",
|
||||||
"SettingsTabSystemExpandDramSize": "Expand DRAM Size to 6GiB",
|
"SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)",
|
||||||
"SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services",
|
"SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services",
|
||||||
"SettingsTabGraphics": "Graphics",
|
"SettingsTabGraphics": "Graphics",
|
||||||
"SettingsTabGraphicsAPI": "Graphics API",
|
"SettingsTabGraphicsAPI": "Graphics API",
|
||||||
@@ -440,7 +440,7 @@
|
|||||||
"MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.",
|
"MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.",
|
||||||
"MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.",
|
"MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.",
|
||||||
"MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.",
|
"MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.",
|
||||||
"DRamTooltip": "Increases the amount of memory on the emulated system from 4GiB to 6GiB.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.",
|
"DRamTooltip": "Utilizes an alternative MemoryMode layout to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.",
|
||||||
"IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.",
|
"IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.",
|
||||||
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||||
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||||
|
@@ -36,7 +36,7 @@
|
|||||||
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
|
||||||
<PackageReference Include="SPB" Version="0.0.4-build27" />
|
<PackageReference Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -460,8 +460,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
|
|
||||||
|
|
||||||
if (gamepad != null)
|
if (gamepad != null)
|
||||||
{
|
{
|
||||||
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
|
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
|
||||||
@@ -472,8 +470,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
|
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
|
|
||||||
|
|
||||||
if (gamepad != null)
|
if (gamepad != null)
|
||||||
{
|
{
|
||||||
if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
|
if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
|
||||||
|
@@ -27,7 +27,7 @@ namespace Ryujinx.Cpu
|
|||||||
long TpidrroEl0 { get; set; }
|
long TpidrroEl0 { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processor State register.
|
/// Processor State Register.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint Pstate { get; set; }
|
uint Pstate { get; set; }
|
||||||
|
|
||||||
@@ -109,4 +109,4 @@ namespace Ryujinx.Cpu
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
void StopRunning();
|
void StopRunning();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly bool SupportsFragmentShaderOrderingIntel;
|
public readonly bool SupportsFragmentShaderOrderingIntel;
|
||||||
public readonly bool SupportsGeometryShaderPassthrough;
|
public readonly bool SupportsGeometryShaderPassthrough;
|
||||||
public readonly bool SupportsImageLoadFormatted;
|
public readonly bool SupportsImageLoadFormatted;
|
||||||
|
public readonly bool SupportsLayerVertexTessellation;
|
||||||
public readonly bool SupportsMismatchingViewFormat;
|
public readonly bool SupportsMismatchingViewFormat;
|
||||||
public readonly bool SupportsCubemapView;
|
public readonly bool SupportsCubemapView;
|
||||||
public readonly bool SupportsNonConstantTextureOffset;
|
public readonly bool SupportsNonConstantTextureOffset;
|
||||||
@@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
bool supportsFragmentShaderOrderingIntel,
|
bool supportsFragmentShaderOrderingIntel,
|
||||||
bool supportsGeometryShaderPassthrough,
|
bool supportsGeometryShaderPassthrough,
|
||||||
bool supportsImageLoadFormatted,
|
bool supportsImageLoadFormatted,
|
||||||
|
bool supportsLayerVertexTessellation,
|
||||||
bool supportsMismatchingViewFormat,
|
bool supportsMismatchingViewFormat,
|
||||||
bool supportsCubemapView,
|
bool supportsCubemapView,
|
||||||
bool supportsNonConstantTextureOffset,
|
bool supportsNonConstantTextureOffset,
|
||||||
@@ -86,6 +88,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
|
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
|
||||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||||
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
||||||
|
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
||||||
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
||||||
SupportsCubemapView = supportsCubemapView;
|
SupportsCubemapView = supportsCubemapView;
|
||||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||||
|
@@ -95,5 +95,10 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// Byte alignment for block linear textures
|
/// Byte alignment for block linear textures
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int GobAlignment = 64;
|
public const int GobAlignment = 64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expected byte alignment for storage buffers
|
||||||
|
/// </summary>
|
||||||
|
public const int StorageAlignment = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -138,7 +138,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
qmd.CtaThreadDimension1,
|
qmd.CtaThreadDimension1,
|
||||||
qmd.CtaThreadDimension2,
|
qmd.CtaThreadDimension2,
|
||||||
localMemorySize,
|
localMemorySize,
|
||||||
sharedMemorySize);
|
sharedMemorySize,
|
||||||
|
_channel.BufferManager.HasUnalignedStorageBuffers);
|
||||||
|
|
||||||
CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa);
|
CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa);
|
||||||
|
|
||||||
@@ -150,6 +151,33 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
|
|
||||||
ShaderProgramInfo info = cs.Shaders[0].Info;
|
ShaderProgramInfo info = cs.Shaders[0].Info;
|
||||||
|
|
||||||
|
bool hasUnaligned = _channel.BufferManager.HasUnalignedStorageBuffers;
|
||||||
|
|
||||||
|
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||||
|
{
|
||||||
|
BufferDescriptor sb = info.SBuffers[index];
|
||||||
|
|
||||||
|
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0);
|
||||||
|
|
||||||
|
int sbDescOffset = 0x310 + sb.Slot * 0x10;
|
||||||
|
|
||||||
|
sbDescAddress += (ulong)sbDescOffset;
|
||||||
|
|
||||||
|
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
|
_channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned)
|
||||||
|
{
|
||||||
|
// Refetch the shader, as assumptions about storage buffer alignment have changed.
|
||||||
|
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa);
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||||
|
|
||||||
|
info = cs.Shaders[0].Info;
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < info.CBuffers.Count; index++)
|
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||||
{
|
{
|
||||||
BufferDescriptor cb = info.CBuffers[index];
|
BufferDescriptor cb = info.CBuffers[index];
|
||||||
@@ -174,21 +202,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
_channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
_channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
|
||||||
{
|
|
||||||
BufferDescriptor sb = info.SBuffers[index];
|
|
||||||
|
|
||||||
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0);
|
|
||||||
|
|
||||||
int sbDescOffset = 0x310 + sb.Slot * 0x10;
|
|
||||||
|
|
||||||
sbDescAddress += (ulong)sbDescOffset;
|
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
|
||||||
|
|
||||||
_channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
_channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
|
_channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
|
||||||
_channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
|
_channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers);
|
||||||
|
|
||||||
|
@@ -372,7 +372,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0;
|
float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0;
|
||||||
float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0;
|
float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0;
|
||||||
|
|
||||||
engine.UpdateState();
|
engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex));
|
||||||
|
|
||||||
|
_channel.TextureManager.UpdateRenderTargets();
|
||||||
|
|
||||||
int textureId = _state.State.DrawTextureTextureId;
|
int textureId = _state.State.DrawTextureTextureId;
|
||||||
int samplerId = _state.State.DrawTextureSamplerId;
|
int samplerId = _state.State.DrawTextureSamplerId;
|
||||||
|
@@ -293,9 +293,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void CommitBindings()
|
private void CommitBindings()
|
||||||
{
|
{
|
||||||
|
var buffers = _channel.BufferManager;
|
||||||
|
var hasUnaligned = buffers.HasUnalignedStorageBuffers;
|
||||||
|
|
||||||
UpdateStorageBuffers();
|
UpdateStorageBuffers();
|
||||||
|
|
||||||
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState))
|
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || (buffers.HasUnalignedStorageBuffers != hasUnaligned))
|
||||||
{
|
{
|
||||||
// Shader must be reloaded.
|
// Shader must be reloaded.
|
||||||
UpdateShaderState();
|
UpdateShaderState();
|
||||||
@@ -1361,7 +1364,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_state.State.AlphaTestFunc,
|
_state.State.AlphaTestFunc,
|
||||||
_state.State.AlphaTestRef,
|
_state.State.AlphaTestRef,
|
||||||
ref attributeTypes,
|
ref attributeTypes,
|
||||||
_drawState.HasConstantBufferDrawParameters);
|
_drawState.HasConstantBufferDrawParameters,
|
||||||
|
_channel.BufferManager.HasUnalignedStorageBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
||||||
TextureManager.ReloadPools();
|
TextureManager.ReloadPools();
|
||||||
|
MemoryManager.Physical.BufferCache.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -77,6 +78,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
|
private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
|
||||||
{
|
{
|
||||||
TextureManager.ReloadPools();
|
TextureManager.ReloadPools();
|
||||||
|
MemoryManager.Physical.BufferCache.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -131,6 +133,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
||||||
oldMemoryManager.Physical.DecrementReferenceCount();
|
oldMemoryManager.Physical.DecrementReferenceCount();
|
||||||
|
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
|
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
|
||||||
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
|
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
|
||||||
|
private bool _pruneCaches;
|
||||||
|
|
||||||
public event Action NotifyBuffersModified;
|
public event Action NotifyBuffersModified;
|
||||||
|
|
||||||
@@ -136,6 +137,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the buffer</param>
|
/// <param name="size">Size in bytes of the buffer</param>
|
||||||
public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
|
if (_pruneCaches)
|
||||||
|
{
|
||||||
|
Prune();
|
||||||
|
}
|
||||||
|
|
||||||
if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||||
result.EndGpuAddress < gpuVa + size ||
|
result.EndGpuAddress < gpuVa + size ||
|
||||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||||
@@ -158,17 +164,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <returns>True if modified, false otherwise</returns>
|
/// <returns>True if modified, false otherwise</returns>
|
||||||
public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
|
public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
|
||||||
{
|
{
|
||||||
if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
if (_pruneCaches)
|
||||||
result.EndGpuAddress < gpuVa + size ||
|
|
||||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
|
||||||
{
|
{
|
||||||
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
|
Prune();
|
||||||
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
|
|
||||||
|
|
||||||
_modifiedCache[gpuVa] = result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outAddr = result.Address;
|
// Align the address to avoid creating too many entries on the quick lookup dictionary.
|
||||||
|
ulong mask = BufferAlignmentMask;
|
||||||
|
ulong alignedGpuVa = gpuVa & (~mask);
|
||||||
|
ulong alignedEndGpuVa = (gpuVa + size + mask) & (~mask);
|
||||||
|
|
||||||
|
size = alignedEndGpuVa - alignedGpuVa;
|
||||||
|
|
||||||
|
if (!_modifiedCache.TryGetValue(alignedGpuVa, out BufferCacheEntry result) ||
|
||||||
|
result.EndGpuAddress < alignedEndGpuVa ||
|
||||||
|
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||||
|
{
|
||||||
|
ulong address = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size);
|
||||||
|
result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size));
|
||||||
|
|
||||||
|
_modifiedCache[alignedGpuVa] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
outAddr = result.Address | (gpuVa & mask);
|
||||||
|
|
||||||
return result.Buffer.IsModified(result.Address, size);
|
return result.Buffer.IsModified(result.Address, size);
|
||||||
}
|
}
|
||||||
@@ -435,6 +453,54 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prune any invalid entries from a quick access dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dictionary">Dictionary to prune</param>
|
||||||
|
/// <param name="toDelete">List used to track entries to delete</param>
|
||||||
|
private void Prune(Dictionary<ulong, BufferCacheEntry> dictionary, ref List<ulong> toDelete)
|
||||||
|
{
|
||||||
|
foreach (var entry in dictionary)
|
||||||
|
{
|
||||||
|
if (entry.Value.UnmappedSequence != entry.Value.Buffer.UnmappedSequence)
|
||||||
|
{
|
||||||
|
(toDelete ??= new()).Add(entry.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toDelete != null)
|
||||||
|
{
|
||||||
|
foreach (ulong entry in toDelete)
|
||||||
|
{
|
||||||
|
dictionary.Remove(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prune any invalid entries from the quick access dictionaries.
|
||||||
|
/// </summary>
|
||||||
|
private void Prune()
|
||||||
|
{
|
||||||
|
List<ulong> toDelete = null;
|
||||||
|
|
||||||
|
Prune(_dirtyCache, ref toDelete);
|
||||||
|
|
||||||
|
toDelete?.Clear();
|
||||||
|
|
||||||
|
Prune(_modifiedCache, ref toDelete);
|
||||||
|
|
||||||
|
_pruneCaches = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a prune of invalid entries the next time a dictionary cache is accessed.
|
||||||
|
/// </summary>
|
||||||
|
public void QueuePrune()
|
||||||
|
{
|
||||||
|
_pruneCaches = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes all buffers in the cache.
|
/// Disposes all buffers in the cache.
|
||||||
/// It's an error to use the buffer manager after disposal.
|
/// It's an error to use the buffer manager after disposal.
|
||||||
|
@@ -17,6 +17,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
|
|
||||||
|
private int _unalignedStorageBuffers;
|
||||||
|
public bool HasUnalignedStorageBuffers => _unalignedStorageBuffers > 0;
|
||||||
|
|
||||||
private IndexBuffer _indexBuffer;
|
private IndexBuffer _indexBuffer;
|
||||||
private readonly VertexBuffer[] _vertexBuffers;
|
private readonly VertexBuffer[] _vertexBuffers;
|
||||||
private readonly BufferBounds[] _transformFeedbackBuffers;
|
private readonly BufferBounds[] _transformFeedbackBuffers;
|
||||||
@@ -38,6 +41,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public BufferBounds[] Buffers { get; }
|
public BufferBounds[] Buffers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag indicating if this binding is unaligned.
|
||||||
|
/// </summary>
|
||||||
|
public bool[] Unaligned { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total amount of buffers used on the shader.
|
/// Total amount of buffers used on the shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -51,6 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
Bindings = new BufferDescriptor[count];
|
Bindings = new BufferDescriptor[count];
|
||||||
Buffers = new BufferBounds[count];
|
Buffers = new BufferBounds[count];
|
||||||
|
Unaligned = new bool[count];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -202,6 +211,31 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
_transformFeedbackBuffersDirty = true;
|
_transformFeedbackBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records the alignment of a storage buffer.
|
||||||
|
/// Unaligned storage buffers disable some optimizations on the shader.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffers">The binding list to modify</param>
|
||||||
|
/// <param name="index">Index of the storage buffer</param>
|
||||||
|
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||||
|
private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa)
|
||||||
|
{
|
||||||
|
bool unaligned = (gpuVa & (Constants.StorageAlignment - 1)) != 0;
|
||||||
|
|
||||||
|
if (unaligned || HasUnalignedStorageBuffers)
|
||||||
|
{
|
||||||
|
// Check if the alignment changed for this binding.
|
||||||
|
|
||||||
|
ref bool currentUnaligned = ref buffers.Unaligned[index];
|
||||||
|
|
||||||
|
if (currentUnaligned != unaligned)
|
||||||
|
{
|
||||||
|
currentUnaligned = unaligned;
|
||||||
|
_unalignedStorageBuffers += unaligned ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets a storage buffer on the compute pipeline.
|
/// Sets a storage buffer on the compute pipeline.
|
||||||
/// Storage buffers can be read and written to on shaders.
|
/// Storage buffers can be read and written to on shaders.
|
||||||
@@ -214,6 +248,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
|
size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
|
||||||
|
|
||||||
|
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
|
||||||
|
|
||||||
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
||||||
|
|
||||||
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
@@ -234,17 +270,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
|
size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
|
||||||
|
|
||||||
|
BuffersPerStage buffers = _gpStorageBuffers[stage];
|
||||||
|
|
||||||
|
RecordStorageAlignment(buffers, index, gpuVa);
|
||||||
|
|
||||||
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
||||||
|
|
||||||
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
|
if (buffers.Buffers[index].Address != address ||
|
||||||
_gpStorageBuffers[stage].Buffers[index].Size != size)
|
buffers.Buffers[index].Size != size)
|
||||||
{
|
{
|
||||||
_gpStorageBuffersDirty = true;
|
_gpStorageBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_gpStorageBuffers[stage].SetBounds(index, address, size, flags);
|
buffers.SetBounds(index, address, size, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -325,13 +325,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public void ReregisterRanges(Action<ulong, ulong> rangeAction)
|
public void ReregisterRanges(Action<ulong, ulong> rangeAction)
|
||||||
{
|
{
|
||||||
ref var ranges = ref ThreadStaticArray<BufferModifiedRange>.Get();
|
ref var ranges = ref ThreadStaticArray<BufferModifiedRange>.Get();
|
||||||
|
int count;
|
||||||
|
|
||||||
// Range list must be consistent for this operation.
|
// Range list must be consistent for this operation.
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (ranges.Length < Count)
|
count = Count;
|
||||||
|
if (ranges.Length < count)
|
||||||
{
|
{
|
||||||
Array.Resize(ref ranges, Count);
|
Array.Resize(ref ranges, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -342,7 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong currentSync = _context.SyncNumber;
|
ulong currentSync = _context.SyncNumber;
|
||||||
for (int i = 0; i < Count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange range = ranges[i];
|
BufferModifiedRange range = ranges[i];
|
||||||
if (range.SyncNumber != currentSync)
|
if (range.SyncNumber != currentSync)
|
||||||
|
@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="computeState">Compute state</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the compute shader</param>
|
/// <param name="gpuVa">GPU virtual address of the compute shader</param>
|
||||||
/// <param name="program">Cached host program for the given state, if found</param>
|
/// <param name="program">Cached host program for the given state, if found</param>
|
||||||
/// <param name="cachedGuestCode">Cached guest code, if any found</param>
|
/// <param name="cachedGuestCode">Cached guest code, if any found</param>
|
||||||
@@ -43,6 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
public bool TryFind(
|
public bool TryFind(
|
||||||
GpuChannel channel,
|
GpuChannel channel,
|
||||||
GpuChannelPoolState poolState,
|
GpuChannelPoolState poolState,
|
||||||
|
GpuChannelComputeState computeState,
|
||||||
ulong gpuVa,
|
ulong gpuVa,
|
||||||
out CachedShaderProgram program,
|
out CachedShaderProgram program,
|
||||||
out byte[] cachedGuestCode)
|
out byte[] cachedGuestCode)
|
||||||
@@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
program = null;
|
program = null;
|
||||||
ShaderCodeAccessor codeAccessor = new ShaderCodeAccessor(channel.MemoryManager, gpuVa);
|
ShaderCodeAccessor codeAccessor = new ShaderCodeAccessor(channel.MemoryManager, gpuVa);
|
||||||
bool hasSpecList = _cache.TryFindItem(codeAccessor, out var specList, out cachedGuestCode);
|
bool hasSpecList = _cache.TryFindItem(codeAccessor, out var specList, out cachedGuestCode);
|
||||||
return hasSpecList && specList.TryFindForCompute(channel, poolState, out program);
|
return hasSpecList && specList.TryFindForCompute(channel, poolState, computeState, out program);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -225,6 +225,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
return _oldSpecState.GraphicsState.EarlyZForce;
|
return _oldSpecState.GraphicsState.EarlyZForce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryHasUnalignedStorageBuffer()
|
||||||
|
{
|
||||||
|
return _oldSpecState.GraphicsState.HasUnalignedStorageBuffer || _oldSpecState.ComputeState.HasUnalignedStorageBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool QueryViewportTransformDisable()
|
public bool QueryViewportTransformDisable()
|
||||||
{
|
{
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 3747;
|
private const uint CodeGenVersion = 3868;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
@@ -379,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
|
|
||||||
if (context.Capabilities.Api == TargetApi.Vulkan)
|
if (context.Capabilities.Api == TargetApi.Vulkan)
|
||||||
{
|
{
|
||||||
ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode, isCompute);
|
ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode);
|
||||||
|
|
||||||
hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
||||||
}
|
}
|
||||||
|
@@ -636,6 +636,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
||||||
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
||||||
|
|
||||||
|
TranslatorContext previousStage = null;
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||||
{
|
{
|
||||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||||
@@ -668,6 +670,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
{
|
{
|
||||||
translatedStages.Add(program);
|
translatedStages.Add(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previousStage = currentStage;
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
previousStage != null &&
|
||||||
|
previousStage.LayerOutputWritten &&
|
||||||
|
stageIndex == 3 &&
|
||||||
|
!_context.Capabilities.SupportsLayerVertexTessellation)
|
||||||
|
{
|
||||||
|
translatedStages.Add(previousStage.GenerateGeometryPassthrough());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
@@ -12,8 +14,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
using MemoryStream output = new MemoryStream();
|
using MemoryStream output = new MemoryStream();
|
||||||
using BinaryWriter writer = new BinaryWriter(output);
|
using BinaryWriter writer = new BinaryWriter(output);
|
||||||
|
|
||||||
|
writer.Write(sources.Length);
|
||||||
|
|
||||||
for (int i = 0; i < sources.Length; i++)
|
for (int i = 0; i < sources.Length; i++)
|
||||||
{
|
{
|
||||||
|
writer.Write((int)sources[i].Stage);
|
||||||
writer.Write(sources[i].BinaryCode.Length);
|
writer.Write(sources[i].BinaryCode.Length);
|
||||||
writer.Write(sources[i].BinaryCode);
|
writer.Write(sources[i].BinaryCode);
|
||||||
}
|
}
|
||||||
@@ -21,29 +26,40 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code, bool compute)
|
public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code)
|
||||||
{
|
{
|
||||||
using MemoryStream input = new MemoryStream(code);
|
using MemoryStream input = new MemoryStream(code);
|
||||||
using BinaryReader reader = new BinaryReader(input);
|
using BinaryReader reader = new BinaryReader(input);
|
||||||
|
|
||||||
List<ShaderSource> output = new List<ShaderSource>();
|
List<ShaderSource> output = new List<ShaderSource>();
|
||||||
|
|
||||||
for (int i = compute ? 0 : 1; i < stages.Length; i++)
|
int count = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
CachedShaderStage stage = stages[i];
|
ShaderStage stage = (ShaderStage)reader.ReadInt32();
|
||||||
|
|
||||||
if (stage == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int binaryCodeLength = reader.ReadInt32();
|
int binaryCodeLength = reader.ReadInt32();
|
||||||
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
||||||
|
|
||||||
output.Add(new ShaderSource(binaryCode, ShaderCache.GetBindings(stage.Info), stage.Info.Stage, TargetLanguage.Spirv));
|
output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv));
|
||||||
}
|
}
|
||||||
|
|
||||||
return output.ToArray();
|
return output.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < stages.Length; i++)
|
||||||
|
{
|
||||||
|
CachedShaderStage currentStage = stages[i];
|
||||||
|
|
||||||
|
if (currentStage != null && currentStage.Info.Stage == stage && currentStage.Info != null)
|
||||||
|
{
|
||||||
|
return ShaderCache.GetBindings(currentStage.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -145,6 +145,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return _state.GraphicsState.HasConstantBufferDrawParameters;
|
return _state.GraphicsState.HasConstantBufferDrawParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool QueryHasUnalignedStorageBuffer()
|
||||||
|
{
|
||||||
|
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public InputTopology QueryPrimitiveTopology()
|
public InputTopology QueryPrimitiveTopology()
|
||||||
{
|
{
|
||||||
|
@@ -128,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsLayerVertexTessellation() => _context.Capabilities.SupportsLayerVertexTessellation;
|
||||||
|
|
||||||
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
|
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
|
||||||
|
|
||||||
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
|
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
|
||||||
|
@@ -32,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int SharedMemorySize;
|
public readonly int SharedMemorySize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that any storage buffer use is unaligned.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool HasUnalignedStorageBuffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU compute state.
|
/// Creates a new GPU compute state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="localSizeZ">Local group size Z of the compute shader</param>
|
/// <param name="localSizeZ">Local group size Z of the compute shader</param>
|
||||||
/// <param name="localMemorySize">Local memory size of the compute shader</param>
|
/// <param name="localMemorySize">Local memory size of the compute shader</param>
|
||||||
/// <param name="sharedMemorySize">Shared memory size of the compute shader</param>
|
/// <param name="sharedMemorySize">Shared memory size of the compute shader</param>
|
||||||
|
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
|
||||||
public GpuChannelComputeState(
|
public GpuChannelComputeState(
|
||||||
int localSizeX,
|
int localSizeX,
|
||||||
int localSizeY,
|
int localSizeY,
|
||||||
int localSizeZ,
|
int localSizeZ,
|
||||||
int localMemorySize,
|
int localMemorySize,
|
||||||
int sharedMemorySize)
|
int sharedMemorySize,
|
||||||
|
bool hasUnalignedStorageBuffer)
|
||||||
{
|
{
|
||||||
LocalSizeX = localSizeX;
|
LocalSizeX = localSizeX;
|
||||||
LocalSizeY = localSizeY;
|
LocalSizeY = localSizeY;
|
||||||
LocalSizeZ = localSizeZ;
|
LocalSizeZ = localSizeZ;
|
||||||
LocalMemorySize = localMemorySize;
|
LocalMemorySize = localMemorySize;
|
||||||
SharedMemorySize = sharedMemorySize;
|
SharedMemorySize = sharedMemorySize;
|
||||||
|
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -82,6 +82,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly bool HasConstantBufferDrawParameters;
|
public readonly bool HasConstantBufferDrawParameters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that any storage buffer use is unaligned.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool HasUnalignedStorageBuffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU graphics state.
|
/// Creates a new GPU graphics state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -99,6 +104,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param>
|
/// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param>
|
||||||
/// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
|
/// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
|
||||||
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
||||||
|
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
|
||||||
public GpuChannelGraphicsState(
|
public GpuChannelGraphicsState(
|
||||||
bool earlyZForce,
|
bool earlyZForce,
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
@@ -113,7 +119,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
CompareOp alphaTestCompare,
|
CompareOp alphaTestCompare,
|
||||||
float alphaTestReference,
|
float alphaTestReference,
|
||||||
ref Array32<AttributeType> attributeTypes,
|
ref Array32<AttributeType> attributeTypes,
|
||||||
bool hasConstantBufferDrawParameters)
|
bool hasConstantBufferDrawParameters,
|
||||||
|
bool hasUnalignedStorageBuffer)
|
||||||
{
|
{
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
Topology = topology;
|
Topology = topology;
|
||||||
@@ -129,6 +136,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
AlphaTestReference = alphaTestReference;
|
AlphaTestReference = alphaTestReference;
|
||||||
AttributeTypes = attributeTypes;
|
AttributeTypes = attributeTypes;
|
||||||
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
||||||
|
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -203,12 +203,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
GpuChannelComputeState computeState,
|
GpuChannelComputeState computeState,
|
||||||
ulong gpuVa)
|
ulong gpuVa)
|
||||||
{
|
{
|
||||||
if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, cpShader, gpuVa))
|
if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
|
||||||
{
|
{
|
||||||
return cpShader;
|
return cpShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_computeShaderCache.TryFind(channel, poolState, gpuVa, out cpShader, out byte[] cachedGuestCode))
|
if (_computeShaderCache.TryFind(channel, poolState, computeState, gpuVa, out cpShader, out byte[] cachedGuestCode))
|
||||||
{
|
{
|
||||||
_cpPrograms[gpuVa] = cpShader;
|
_cpPrograms[gpuVa] = cpShader;
|
||||||
return cpShader;
|
return cpShader;
|
||||||
@@ -356,6 +356,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
||||||
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
||||||
|
|
||||||
|
TranslatorContext previousStage = null;
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||||
{
|
{
|
||||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||||
@@ -392,6 +394,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
shaderSources.Add(CreateShaderSource(program));
|
shaderSources.Add(CreateShaderSource(program));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previousStage = currentStage;
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
previousStage != null &&
|
||||||
|
previousStage.LayerOutputWritten &&
|
||||||
|
stageIndex == 3 &&
|
||||||
|
!_context.Capabilities.SupportsLayerVertexTessellation)
|
||||||
|
{
|
||||||
|
shaderSources.Add(CreateShaderSource(previousStage.GenerateGeometryPassthrough()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,18 +485,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel using the shader</param>
|
/// <param name="channel">GPU channel using the shader</param>
|
||||||
/// <param name="poolState">GPU channel state to verify shader compatibility</param>
|
/// <param name="poolState">GPU channel state to verify shader compatibility</param>
|
||||||
|
/// <param name="computeState">GPU channel compute state to verify shader compatibility</param>
|
||||||
/// <param name="cpShader">Cached compute shader</param>
|
/// <param name="cpShader">Cached compute shader</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
|
/// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
|
||||||
/// <returns>True if the code is different, false otherwise</returns>
|
/// <returns>True if the code is different, false otherwise</returns>
|
||||||
private static bool IsShaderEqual(
|
private static bool IsShaderEqual(
|
||||||
GpuChannel channel,
|
GpuChannel channel,
|
||||||
GpuChannelPoolState poolState,
|
GpuChannelPoolState poolState,
|
||||||
|
GpuChannelComputeState computeState,
|
||||||
CachedShaderProgram cpShader,
|
CachedShaderProgram cpShader,
|
||||||
ulong gpuVa)
|
ulong gpuVa)
|
||||||
{
|
{
|
||||||
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
|
if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
|
||||||
{
|
{
|
||||||
return cpShader.SpecializationState.MatchesCompute(channel, poolState, true);
|
return cpShader.SpecializationState.MatchesCompute(channel, poolState, computeState, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@@ -53,13 +53,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="computeState">Compute state</param>
|
||||||
/// <param name="program">Cached program, if found</param>
|
/// <param name="program">Cached program, if found</param>
|
||||||
/// <returns>True if a compatible program is found, false otherwise</returns>
|
/// <returns>True if a compatible program is found, false otherwise</returns>
|
||||||
public bool TryFindForCompute(GpuChannel channel, GpuChannelPoolState poolState, out CachedShaderProgram program)
|
public bool TryFindForCompute(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelComputeState computeState, out CachedShaderProgram program)
|
||||||
{
|
{
|
||||||
foreach (var entry in _entries)
|
foreach (var entry in _entries)
|
||||||
{
|
{
|
||||||
if (entry.SpecializationState.MatchesCompute(channel, poolState, true))
|
if (entry.SpecializationState.MatchesCompute(channel, poolState, computeState, true))
|
||||||
{
|
{
|
||||||
program = entry;
|
program = entry;
|
||||||
return true;
|
return true;
|
||||||
|
@@ -531,6 +531,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (graphicsState.HasUnalignedStorageBuffer != GraphicsState.HasUnalignedStorageBuffer)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Matches(channel, poolState, checkTextures, isCompute: false);
|
return Matches(channel, poolState, checkTextures, isCompute: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,10 +544,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">GPU channel</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
/// <param name="poolState">Texture pool state</param>
|
/// <param name="poolState">Texture pool state</param>
|
||||||
|
/// <param name="computeState">Compute state</param>
|
||||||
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||||
/// <returns>True if the state matches, false otherwise</returns>
|
/// <returns>True if the state matches, false otherwise</returns>
|
||||||
public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures)
|
public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures)
|
||||||
{
|
{
|
||||||
|
if (computeState.HasUnalignedStorageBuffer != ComputeState.HasUnalignedStorageBuffer)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Matches(channel, poolState, checkTextures, isCompute: true);
|
return Matches(channel, poolState, checkTextures, isCompute: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||||||
short dqv = dq[0];
|
short dqv = dq[0];
|
||||||
ReadOnlySpan<byte> cat6Prob = (xd.Bd == 12)
|
ReadOnlySpan<byte> cat6Prob = (xd.Bd == 12)
|
||||||
? Luts.Vp9Cat6ProbHigh12
|
? Luts.Vp9Cat6ProbHigh12
|
||||||
: (xd.Bd == 10) ? new ReadOnlySpan<byte>(Luts.Vp9Cat6ProbHigh12).Slice(2) : Luts.Vp9Cat6Prob;
|
: (xd.Bd == 10) ? Luts.Vp9Cat6ProbHigh12.Slice(2) : Luts.Vp9Cat6Prob;
|
||||||
int cat6Bits = (xd.Bd == 12) ? 18 : (xd.Bd == 10) ? 16 : 14;
|
int cat6Bits = (xd.Bd == 12) ? 18 : (xd.Bd == 10) ? 16 : 14;
|
||||||
// Keep value, range, and count as locals. The compiler produces better
|
// Keep value, range, and count as locals. The compiler produces better
|
||||||
// results with the locals than using r directly.
|
// results with the locals than using r directly.
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Nvdec.Vp9.Types;
|
using Ryujinx.Graphics.Nvdec.Vp9.Types;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Nvdec.Vp9
|
namespace Ryujinx.Graphics.Nvdec.Vp9
|
||||||
{
|
{
|
||||||
internal static class Luts
|
internal static class Luts
|
||||||
{
|
{
|
||||||
public static readonly byte[] SizeGroupLookup = new byte[]
|
public static ReadOnlySpan<byte> SizeGroupLookup => new byte[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 };
|
||||||
{
|
|
||||||
0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly BlockSize[][] SubsizeLookup = new BlockSize[][]
|
public static readonly BlockSize[][] SubsizeLookup = new BlockSize[][]
|
||||||
{
|
{
|
||||||
@@ -1070,18 +1068,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||||||
-(sbyte)MvClassType.MvClass10,
|
-(sbyte)MvClassType.MvClass10,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly sbyte[] Vp9MvFPTree = new sbyte[] { -0, 2, -1, 4, -2, -3 };
|
public static ReadOnlySpan<sbyte> Vp9MvFPTree => new sbyte[] { -0, 2, -1, 4, -2, -3 };
|
||||||
|
|
||||||
// Entropy
|
// Entropy
|
||||||
|
|
||||||
public static readonly byte[] Vp9Cat1Prob = new byte[] { 159 };
|
public static ReadOnlySpan<byte> Vp9Cat1Prob => new byte[] { 159 };
|
||||||
public static readonly byte[] Vp9Cat2Prob = new byte[] { 165, 145 };
|
public static ReadOnlySpan<byte> Vp9Cat2Prob => new byte[] { 165, 145 };
|
||||||
public static readonly byte[] Vp9Cat3Prob = new byte[] { 173, 148, 140 };
|
public static ReadOnlySpan<byte> Vp9Cat3Prob => new byte[] { 173, 148, 140 };
|
||||||
public static readonly byte[] Vp9Cat4Prob = new byte[] { 176, 155, 140, 135 };
|
public static ReadOnlySpan<byte> Vp9Cat4Prob => new byte[] { 176, 155, 140, 135 };
|
||||||
public static readonly byte[] Vp9Cat5Prob = new byte[] { 180, 157, 141, 134, 130 };
|
public static ReadOnlySpan<byte> Vp9Cat5Prob => new byte[] { 180, 157, 141, 134, 130 };
|
||||||
public static readonly byte[] Vp9Cat6Prob = new byte[] { 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
|
public static ReadOnlySpan<byte> Vp9Cat6Prob => new byte[] { 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
|
||||||
|
|
||||||
public static readonly byte[] Vp9Cat6ProbHigh12 = new byte[]
|
public static ReadOnlySpan<byte> Vp9Cat6ProbHigh12 => new byte[]
|
||||||
{
|
{
|
||||||
255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129
|
255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129
|
||||||
};
|
};
|
||||||
@@ -1131,12 +1129,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
|||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly byte[] Vp9CoefbandTrans4X4 = new byte[]
|
private static ReadOnlySpan<byte> Vp9CoefbandTrans4X4 => new byte[]
|
||||||
{
|
{
|
||||||
0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5,
|
0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static byte[] get_band_translate(TxSize txSize)
|
public static ReadOnlySpan<byte> get_band_translate(TxSize txSize)
|
||||||
{
|
{
|
||||||
return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : Vp9CoefbandTrans8X8Plus;
|
return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : Vp9CoefbandTrans8X8Plus;
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck);
|
private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck);
|
||||||
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
|
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
|
||||||
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
|
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
|
||||||
|
private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array"));
|
||||||
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
|
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
|
||||||
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
|
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
|
||||||
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
|
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
|
||||||
@@ -61,6 +62,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public static bool SupportsQuads => _supportsQuads.Value;
|
public static bool SupportsQuads => _supportsQuads.Value;
|
||||||
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
|
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
|
||||||
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
|
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
|
||||||
|
public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value;
|
||||||
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
|
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
|
||||||
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
|
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
|
||||||
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
|
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
|
||||||
|
@@ -117,12 +117,13 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
||||||
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
|
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
|
||||||
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
||||||
|
supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||||
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
||||||
supportsCubemapView: true,
|
supportsCubemapView: true,
|
||||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||||
supportsViewportIndex: true,
|
supportsViewportIndex: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||||
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
||||||
|
@@ -1829,7 +1829,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D)
|
if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D)
|
||||||
{
|
{
|
||||||
result = ScalingHelpers.ApplyUnscaling(context, texOp, result, isBindless, isIndexed);
|
result = ScalingHelpers.ApplyUnscaling(context, texOp.WithType(type), result, isBindless, isIndexed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OperationResult(AggregateType.S32, result);
|
return new OperationResult(AggregateType.S32, result);
|
||||||
|
@@ -10,5 +10,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
public const int NvnBaseVertexByteOffset = 0x640;
|
public const int NvnBaseVertexByteOffset = 0x640;
|
||||||
public const int NvnBaseInstanceByteOffset = 0x644;
|
public const int NvnBaseInstanceByteOffset = 0x644;
|
||||||
public const int NvnDrawIndexByteOffset = 0x648;
|
public const int NvnDrawIndexByteOffset = 0x648;
|
||||||
|
|
||||||
|
public const int StorageAlignment = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -177,6 +177,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries whenever the current draw uses unaligned storage buffer addresses.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if any storage buffer address is not aligned to 16 bytes, false otherwise</returns>
|
||||||
|
bool QueryHasUnalignedStorageBuffer()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -249,6 +258,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host support for writes to Layer from vertex or tessellation shader stages.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if writes to layer from vertex or tessellation are supported, false otherwise</returns>
|
||||||
|
bool QueryHostSupportsLayerVertexTessellation()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host GPU non-constant texture offset support.
|
/// Queries host GPU non-constant texture offset support.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -278,13 +278,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
|
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
|
||||||
{
|
{
|
||||||
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
|
||||||
|
int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1;
|
||||||
|
|
||||||
|
if (attr == AttributeConsts.Layer && config.Stage != ShaderStage.Geometry && !supportsLayerFromVertexOrTess)
|
||||||
{
|
{
|
||||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, 0, isOutput);
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput);
|
||||||
|
config.SetLayerOutputAttribute(attr);
|
||||||
|
}
|
||||||
|
else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
||||||
|
{
|
||||||
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr, isOutput);
|
||||||
}
|
}
|
||||||
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
||||||
{
|
{
|
||||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, 4, isOutput);
|
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 4, isOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attr;
|
return attr;
|
||||||
|
@@ -27,5 +27,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
CbufSlot = cbufSlot;
|
CbufSlot = cbufSlot;
|
||||||
Handle = handle;
|
Handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AstTextureOperation WithType(SamplerType type)
|
||||||
|
{
|
||||||
|
return new AstTextureOperation(Inst, type, Format, Flags, CbufSlot, Handle, Index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// we can guess which storage buffer it is accessing.
|
// we can guess which storage buffer it is accessing.
|
||||||
// We can then replace the global memory access with a storage
|
// We can then replace the global memory access with a storage
|
||||||
// buffer access.
|
// buffer access.
|
||||||
node = ReplaceGlobalWithStorage(node, config, storageIndex);
|
node = ReplaceGlobalWithStorage(block, node, config, storageIndex);
|
||||||
}
|
}
|
||||||
else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal)
|
else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal)
|
||||||
{
|
{
|
||||||
@@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkedListNode<INode> ReplaceGlobalWithStorage(LinkedListNode<INode> node, ShaderConfig config, int storageIndex)
|
private static LinkedListNode<INode> ReplaceGlobalWithStorage(BasicBlock block, LinkedListNode<INode> node, ShaderConfig config, int storageIndex)
|
||||||
{
|
{
|
||||||
Operation operation = (Operation)node.Value;
|
Operation operation = (Operation)node.Value;
|
||||||
|
|
||||||
@@ -64,42 +64,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
|
|
||||||
config.SetUsedStorageBuffer(storageIndex, isWrite);
|
config.SetUsedStorageBuffer(storageIndex, isWrite);
|
||||||
|
|
||||||
Operand GetStorageOffset()
|
|
||||||
{
|
|
||||||
Operand addrLow = operation.GetSource(0);
|
|
||||||
|
|
||||||
Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex));
|
|
||||||
|
|
||||||
Operand baseAddrTrunc = Local();
|
|
||||||
|
|
||||||
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
|
|
||||||
|
|
||||||
Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask);
|
|
||||||
|
|
||||||
node.List.AddBefore(node, andOp);
|
|
||||||
|
|
||||||
Operand byteOffset = Local();
|
|
||||||
Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc);
|
|
||||||
|
|
||||||
node.List.AddBefore(node, subOp);
|
|
||||||
|
|
||||||
if (isStg16Or8)
|
|
||||||
{
|
|
||||||
return byteOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand wordOffset = Local();
|
|
||||||
Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2));
|
|
||||||
|
|
||||||
node.List.AddBefore(node, shrOp);
|
|
||||||
|
|
||||||
return wordOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] sources = new Operand[operation.SourcesCount];
|
Operand[] sources = new Operand[operation.SourcesCount];
|
||||||
|
|
||||||
sources[0] = Const(storageIndex);
|
sources[0] = Const(storageIndex);
|
||||||
sources[1] = GetStorageOffset();
|
sources[1] = GetStorageOffset(block, node, config, storageIndex, operation.GetSource(0), isStg16Or8);
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
for (int index = 2; index < operation.SourcesCount; index++)
|
||||||
{
|
{
|
||||||
@@ -144,6 +112,170 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand GetStorageOffset(
|
||||||
|
BasicBlock block,
|
||||||
|
LinkedListNode<INode> node,
|
||||||
|
ShaderConfig config,
|
||||||
|
int storageIndex,
|
||||||
|
Operand addrLow,
|
||||||
|
bool isStg16Or8)
|
||||||
|
{
|
||||||
|
int baseAddressCbOffset = GetStorageCbOffset(config.Stage, storageIndex);
|
||||||
|
|
||||||
|
bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment);
|
||||||
|
|
||||||
|
(Operand byteOffset, int constantOffset) = storageAligned ?
|
||||||
|
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
|
||||||
|
(null, 0);
|
||||||
|
|
||||||
|
if (byteOffset == null)
|
||||||
|
{
|
||||||
|
Operand baseAddrLow = Cbuf(0, baseAddressCbOffset);
|
||||||
|
Operand baseAddrTrunc = Local();
|
||||||
|
|
||||||
|
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
|
||||||
|
|
||||||
|
Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask);
|
||||||
|
|
||||||
|
node.List.AddBefore(node, andOp);
|
||||||
|
|
||||||
|
Operand offset = Local();
|
||||||
|
Operation subOp = new Operation(Instruction.Subtract, offset, addrLow, baseAddrTrunc);
|
||||||
|
|
||||||
|
node.List.AddBefore(node, subOp);
|
||||||
|
|
||||||
|
byteOffset = offset;
|
||||||
|
}
|
||||||
|
else if (constantOffset != 0)
|
||||||
|
{
|
||||||
|
Operand offset = Local();
|
||||||
|
Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset));
|
||||||
|
|
||||||
|
node.List.AddBefore(node, addOp);
|
||||||
|
|
||||||
|
byteOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteOffset != null)
|
||||||
|
{
|
||||||
|
ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStg16Or8)
|
||||||
|
{
|
||||||
|
return byteOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand wordOffset = Local();
|
||||||
|
Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2));
|
||||||
|
|
||||||
|
node.List.AddBefore(node, shrOp);
|
||||||
|
|
||||||
|
return wordOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCb0Offset(Operand operand, int offset)
|
||||||
|
{
|
||||||
|
return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == 0 && operand.GetCbufOffset() == offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReplaceAddressAlignment(LinkedList<INode> list, Operand address, Operand byteOffset, int constantOffset)
|
||||||
|
{
|
||||||
|
// When we emit 16/8-bit LDG, we add extra code to determine the address alignment.
|
||||||
|
// Eliminate the storage buffer base address from this too, leaving only the byte offset.
|
||||||
|
|
||||||
|
foreach (INode useNode in address.UseOps)
|
||||||
|
{
|
||||||
|
if (useNode is Operation op && op.Inst == Instruction.BitwiseAnd)
|
||||||
|
{
|
||||||
|
Operand src1 = op.GetSource(0);
|
||||||
|
Operand src2 = op.GetSource(1);
|
||||||
|
|
||||||
|
int addressIndex = -1;
|
||||||
|
|
||||||
|
if (src1 == address && src2.Type == OperandType.Constant && src2.Value == 3)
|
||||||
|
{
|
||||||
|
addressIndex = 0;
|
||||||
|
}
|
||||||
|
else if (src2 == address && src1.Type == OperandType.Constant && src1.Value == 3)
|
||||||
|
{
|
||||||
|
addressIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addressIndex != -1)
|
||||||
|
{
|
||||||
|
LinkedListNode<INode> node = list.Find(op);
|
||||||
|
|
||||||
|
// Add offset calculation before the use. Needs to be on the same block.
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
Operand offset = Local();
|
||||||
|
Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset));
|
||||||
|
list.AddBefore(node, addOp);
|
||||||
|
|
||||||
|
op.SetSource(addressIndex, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int baseAddressCbOffset)
|
||||||
|
{
|
||||||
|
if (IsCb0Offset(address, baseAddressCbOffset))
|
||||||
|
{
|
||||||
|
// Direct offset: zero.
|
||||||
|
return (Const(0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
(address, int constantOffset) = GetStorageConstantOffset(block, address);
|
||||||
|
|
||||||
|
address = Utils.FindLastOperation(address, block);
|
||||||
|
|
||||||
|
if (IsCb0Offset(address, baseAddressCbOffset))
|
||||||
|
{
|
||||||
|
// Only constant offset
|
||||||
|
return (Const(0), constantOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add)
|
||||||
|
{
|
||||||
|
return (null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand src1 = offsetAdd.GetSource(0);
|
||||||
|
Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block);
|
||||||
|
|
||||||
|
if (IsCb0Offset(src2, baseAddressCbOffset))
|
||||||
|
{
|
||||||
|
return (src1, constantOffset);
|
||||||
|
}
|
||||||
|
else if (IsCb0Offset(src1, baseAddressCbOffset))
|
||||||
|
{
|
||||||
|
return (src2, constantOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (Operand, int) GetStorageConstantOffset(BasicBlock block, Operand address)
|
||||||
|
{
|
||||||
|
if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add)
|
||||||
|
{
|
||||||
|
return (address, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand src1 = offsetAdd.GetSource(0);
|
||||||
|
Operand src2 = offsetAdd.GetSource(1);
|
||||||
|
|
||||||
|
if (src2.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
return (address, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (src1, src2.Value);
|
||||||
|
}
|
||||||
|
|
||||||
private static LinkedListNode<INode> ReplaceLdgWithLdc(LinkedListNode<INode> node, ShaderConfig config, int storageIndex)
|
private static LinkedListNode<INode> ReplaceLdgWithLdc(LinkedListNode<INode> node, ShaderConfig config, int storageIndex)
|
||||||
{
|
{
|
||||||
Operation operation = (Operation)node.Value;
|
Operation operation = (Operation)node.Value;
|
||||||
@@ -165,7 +297,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
Operand byteOffset = Local();
|
Operand byteOffset = Local();
|
||||||
Operand wordOffset = Local();
|
Operand wordOffset = Local();
|
||||||
|
|
||||||
Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc);
|
Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc);
|
||||||
Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2));
|
Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2));
|
||||||
|
|
||||||
node.List.AddBefore(node, subOp);
|
node.List.AddBefore(node, subOp);
|
||||||
@@ -260,7 +392,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
{
|
{
|
||||||
if (operand.Type == OperandType.ConstantBuffer)
|
if (operand.Type == OperandType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
int slot = operand.GetCbufSlot();
|
int slot = operand.GetCbufSlot();
|
||||||
int offset = operand.GetCbufOffset();
|
int offset = operand.GetCbufOffset();
|
||||||
|
|
||||||
if (slot == 0 && offset >= sbStart && offset < sbEnd)
|
if (slot == 0 && offset >= sbStart && offset < sbEnd)
|
||||||
|
@@ -48,6 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public int Cb1DataSize { get; private set; }
|
public int Cb1DataSize { get; private set; }
|
||||||
|
|
||||||
|
public bool LayerOutputWritten { get; private set; }
|
||||||
|
public int LayerOutputAttribute { get; private set; }
|
||||||
|
|
||||||
public bool NextUsesFixedFuncAttributes { get; private set; }
|
public bool NextUsesFixedFuncAttributes { get; private set; }
|
||||||
public int UsedInputAttributes { get; private set; }
|
public int UsedInputAttributes { get; private set; }
|
||||||
public int UsedOutputAttributes { get; private set; }
|
public int UsedOutputAttributes { get; private set; }
|
||||||
@@ -131,6 +134,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShaderConfig(
|
||||||
|
ShaderStage stage,
|
||||||
|
OutputTopology outputTopology,
|
||||||
|
int maxOutputVertices,
|
||||||
|
IGpuAccessor gpuAccessor,
|
||||||
|
TranslationOptions options) : this(gpuAccessor, options)
|
||||||
|
{
|
||||||
|
Stage = stage;
|
||||||
|
ThreadsPerInputPrimitive = 1;
|
||||||
|
OutputTopology = outputTopology;
|
||||||
|
MaxOutputVertices = maxOutputVertices;
|
||||||
|
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options)
|
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options)
|
||||||
{
|
{
|
||||||
Stage = header.Stage;
|
Stage = header.Stage;
|
||||||
@@ -240,6 +257,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetLayerOutputAttribute(int attr)
|
||||||
|
{
|
||||||
|
LayerOutputWritten = true;
|
||||||
|
LayerOutputAttribute = attr;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetInputUserAttributeFixedFunc(int index)
|
public void SetInputUserAttributeFixedFunc(int index)
|
||||||
{
|
{
|
||||||
UsedInputAttributes |= 1 << index;
|
UsedInputAttributes |= 1 << index;
|
||||||
@@ -694,5 +717,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShaderProgramInfo CreateProgramInfo()
|
||||||
|
{
|
||||||
|
return new ShaderProgramInfo(
|
||||||
|
GetConstantBufferDescriptors(),
|
||||||
|
GetStorageBufferDescriptors(),
|
||||||
|
GetTextureDescriptors(),
|
||||||
|
GetImageDescriptors(),
|
||||||
|
Stage,
|
||||||
|
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
||||||
|
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
|
||||||
|
UsedFeatures.HasFlag(FeatureFlags.RtLayer),
|
||||||
|
ClipDistancesWritten,
|
||||||
|
OmapTargets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -79,17 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
||||||
|
|
||||||
var info = new ShaderProgramInfo(
|
var info = config.CreateProgramInfo();
|
||||||
config.GetConstantBufferDescriptors(),
|
|
||||||
config.GetStorageBufferDescriptors(),
|
|
||||||
config.GetTextureDescriptors(),
|
|
||||||
config.GetImageDescriptors(),
|
|
||||||
config.Stage,
|
|
||||||
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
|
||||||
config.UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
|
|
||||||
config.UsedFeatures.HasFlag(FeatureFlags.RtLayer),
|
|
||||||
config.ClipDistancesWritten,
|
|
||||||
config.OmapTargets);
|
|
||||||
|
|
||||||
return config.Options.TargetLanguage switch
|
return config.Options.TargetLanguage switch
|
||||||
{
|
{
|
||||||
|
@@ -1,7 +1,12 @@
|
|||||||
using Ryujinx.Graphics.Shader.Decoders;
|
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
|
||||||
|
using Ryujinx.Graphics.Shader.CodeGen.Spirv;
|
||||||
|
using Ryujinx.Graphics.Shader.Decoders;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
using static Ryujinx.Graphics.Shader.Translation.Translator;
|
using static Ryujinx.Graphics.Shader.Translation.Translator;
|
||||||
@@ -18,6 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public ShaderStage Stage => _config.Stage;
|
public ShaderStage Stage => _config.Stage;
|
||||||
public int Size => _config.Size;
|
public int Size => _config.Size;
|
||||||
public int Cb1DataSize => _config.Cb1DataSize;
|
public int Cb1DataSize => _config.Cb1DataSize;
|
||||||
|
public bool LayerOutputWritten => _config.LayerOutputWritten;
|
||||||
|
|
||||||
public IGpuAccessor GpuAccessor => _config.GpuAccessor;
|
public IGpuAccessor GpuAccessor => _config.GpuAccessor;
|
||||||
|
|
||||||
@@ -149,5 +155,94 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
return Translator.Translate(code, _config);
|
return Translator.Translate(code, _config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShaderProgram GenerateGeometryPassthrough()
|
||||||
|
{
|
||||||
|
int outputAttributesMask = _config.UsedOutputAttributes;
|
||||||
|
int layerOutputAttr = _config.LayerOutputAttribute;
|
||||||
|
|
||||||
|
OutputTopology outputTopology;
|
||||||
|
int maxOutputVertices;
|
||||||
|
|
||||||
|
switch (GpuAccessor.QueryPrimitiveTopology())
|
||||||
|
{
|
||||||
|
case InputTopology.Points:
|
||||||
|
outputTopology = OutputTopology.PointList;
|
||||||
|
maxOutputVertices = 1;
|
||||||
|
break;
|
||||||
|
case InputTopology.Lines:
|
||||||
|
case InputTopology.LinesAdjacency:
|
||||||
|
outputTopology = OutputTopology.LineStrip;
|
||||||
|
maxOutputVertices = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outputTopology = OutputTopology.TriangleStrip;
|
||||||
|
maxOutputVertices = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderConfig config = new ShaderConfig(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options);
|
||||||
|
|
||||||
|
EmitterContext context = new EmitterContext(default, config, false);
|
||||||
|
|
||||||
|
for (int v = 0; v < maxOutputVertices; v++)
|
||||||
|
{
|
||||||
|
int outAttrsMask = outputAttributesMask;
|
||||||
|
|
||||||
|
while (outAttrsMask != 0)
|
||||||
|
{
|
||||||
|
int attrIndex = BitOperations.TrailingZeroCount(outAttrsMask);
|
||||||
|
|
||||||
|
outAttrsMask &= ~(1 << attrIndex);
|
||||||
|
|
||||||
|
for (int c = 0; c < 4; c++)
|
||||||
|
{
|
||||||
|
int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4;
|
||||||
|
|
||||||
|
Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
|
||||||
|
|
||||||
|
if (attr == layerOutputAttr)
|
||||||
|
{
|
||||||
|
context.Copy(Attribute(AttributeConsts.Layer), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Copy(Attribute(attr), value);
|
||||||
|
config.SetOutputUserAttribute(attrIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.SetInputUserAttribute(attrIndex, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = 0; c < 4; c++)
|
||||||
|
{
|
||||||
|
int attr = AttributeConsts.PositionX + c * 4;
|
||||||
|
|
||||||
|
Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
|
||||||
|
|
||||||
|
context.Copy(Attribute(attr), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.EmitVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.EndPrimitive();
|
||||||
|
|
||||||
|
var operations = context.GetOperations();
|
||||||
|
var cfg = ControlFlowGraph.Create(operations);
|
||||||
|
var function = new Function(cfg.Blocks, "main", false, 0, 0);
|
||||||
|
|
||||||
|
var sInfo = StructuredProgram.MakeStructuredProgram(new[] { function }, config);
|
||||||
|
|
||||||
|
var info = config.CreateProgramInfo();
|
||||||
|
|
||||||
|
return config.Options.TargetLanguage switch
|
||||||
|
{
|
||||||
|
TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)),
|
||||||
|
TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)),
|
||||||
|
_ => throw new NotImplementedException(config.Options.TargetLanguage.ToString())
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -210,7 +210,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cbs != null && !(_buffer.HasCommandBufferDependency(cbs.Value) && _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
if (cbs != null &&
|
||||||
|
_gd.PipelineInternal.RenderPassActive &&
|
||||||
|
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
|
||||||
|
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
||||||
{
|
{
|
||||||
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
|
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
|
||||||
// This avoids ending and beginning render passes on each buffer data upload.
|
// This avoids ending and beginning render passes on each buffer data upload.
|
||||||
|
@@ -130,6 +130,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
1f));
|
1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
Span<byte> dummyTextureData = stackalloc byte[4];
|
||||||
|
_dummyTexture.SetData(dummyTextureData);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetProgram(ShaderCollection program)
|
public void SetProgram(ShaderCollection program)
|
||||||
{
|
{
|
||||||
_program = program;
|
_program = program;
|
||||||
|
@@ -49,7 +49,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private Auto<DisposableFramebuffer> _framebuffer;
|
private Auto<DisposableFramebuffer> _framebuffer;
|
||||||
private Auto<DisposableRenderPass> _renderPass;
|
private Auto<DisposableRenderPass> _renderPass;
|
||||||
private int _writtenAttachmentCount;
|
private int _writtenAttachmentCount;
|
||||||
private bool _renderPassActive;
|
|
||||||
|
|
||||||
private readonly DescriptorSetUpdater _descriptorSetUpdater;
|
private readonly DescriptorSetUpdater _descriptorSetUpdater;
|
||||||
|
|
||||||
@@ -73,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private PipelineColorBlendAttachmentState[] _storedBlend;
|
private PipelineColorBlendAttachmentState[] _storedBlend;
|
||||||
|
|
||||||
public ulong DrawCount { get; private set; }
|
public ulong DrawCount { get; private set; }
|
||||||
|
public bool RenderPassActive { get; private set; }
|
||||||
|
|
||||||
public unsafe PipelineBase(VulkanRenderer gd, Device device)
|
public unsafe PipelineBase(VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
@@ -114,6 +114,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
_descriptorSetUpdater.Initialize();
|
||||||
|
|
||||||
SupportBufferUpdater = new SupportBufferUpdater(Gd);
|
SupportBufferUpdater = new SupportBufferUpdater(Gd);
|
||||||
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
|
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
|
||||||
|
|
||||||
@@ -838,6 +840,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
stages.CopyTo(_newState.Stages.AsSpan().Slice(0, stages.Length));
|
stages.CopyTo(_newState.Stages.AsSpan().Slice(0, stages.Length));
|
||||||
|
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
|
|
||||||
|
if (_program.IsCompute)
|
||||||
|
{
|
||||||
|
EndRenderPass();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Specialize<T>(in T data) where T : unmanaged
|
public void Specialize<T>(in T data) where T : unmanaged
|
||||||
@@ -1451,7 +1458,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private unsafe void BeginRenderPass()
|
private unsafe void BeginRenderPass()
|
||||||
{
|
{
|
||||||
if (!_renderPassActive)
|
if (!RenderPassActive)
|
||||||
{
|
{
|
||||||
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
||||||
var clearValue = new ClearValue();
|
var clearValue = new ClearValue();
|
||||||
@@ -1467,18 +1474,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
};
|
};
|
||||||
|
|
||||||
Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
|
Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
|
||||||
_renderPassActive = true;
|
RenderPassActive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndRenderPass()
|
public void EndRenderPass()
|
||||||
{
|
{
|
||||||
if (_renderPassActive)
|
if (RenderPassActive)
|
||||||
{
|
{
|
||||||
PauseTransformFeedbackInternal();
|
PauseTransformFeedbackInternal();
|
||||||
Gd.Api.CmdEndRenderPass(CommandBuffer);
|
Gd.Api.CmdEndRenderPass(CommandBuffer);
|
||||||
SignalRenderPassEnd();
|
SignalRenderPassEnd();
|
||||||
_renderPassActive = false;
|
RenderPassActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public bool HasMinimalLayout { get; }
|
public bool HasMinimalLayout { get; }
|
||||||
public bool UsePushDescriptors { get; }
|
public bool UsePushDescriptors { get; }
|
||||||
|
public bool IsCompute { get; }
|
||||||
|
|
||||||
public uint Stages { get; }
|
public uint Stages { get; }
|
||||||
|
|
||||||
@@ -47,7 +48,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private VulkanRenderer _gd;
|
private VulkanRenderer _gd;
|
||||||
private Device _device;
|
private Device _device;
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
private bool _isCompute;
|
|
||||||
|
|
||||||
private ProgramPipelineState _state;
|
private ProgramPipelineState _state;
|
||||||
private DisposableRenderPass _dummyRenderPass;
|
private DisposableRenderPass _dummyRenderPass;
|
||||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit)
|
if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit)
|
||||||
{
|
{
|
||||||
_isCompute = true;
|
IsCompute = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internalShaders[i] = shader;
|
internalShaders[i] = shader;
|
||||||
@@ -163,7 +163,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isCompute)
|
if (IsCompute)
|
||||||
{
|
{
|
||||||
CreateBackgroundComputePipeline();
|
CreateBackgroundComputePipeline();
|
||||||
}
|
}
|
||||||
|
@@ -396,6 +396,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsFragmentShaderOrderingIntel: false,
|
supportsFragmentShaderOrderingIntel: false,
|
||||||
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
||||||
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
||||||
|
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
|
||||||
supportsMismatchingViewFormat: true,
|
supportsMismatchingViewFormat: true,
|
||||||
supportsCubemapView: !IsAmdGcn,
|
supportsCubemapView: !IsAmdGcn,
|
||||||
supportsNonConstantTextureOffset: false,
|
supportsNonConstantTextureOffset: false,
|
||||||
|
@@ -172,9 +172,11 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
var fsServer = new FileSystemServer(fsServerClient);
|
||||||
|
|
||||||
DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer);
|
RandomDataGenerator randomGenerator = buffer => Random.Shared.NextBytes(buffer);
|
||||||
|
|
||||||
// Use our own encrypted fs creator that always uses all-zero keys
|
DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer, randomGenerator);
|
||||||
|
|
||||||
|
// Use our own encrypted fs creator that doesn't actually do any encryption
|
||||||
fsServerObjects.FsCreators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator();
|
fsServerObjects.FsCreators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator();
|
||||||
|
|
||||||
GameCard = fsServerObjects.GameCard;
|
GameCard = fsServerObjects.GameCard;
|
||||||
@@ -186,7 +188,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
DeviceOperator = fsServerObjects.DeviceOperator,
|
DeviceOperator = fsServerObjects.DeviceOperator,
|
||||||
ExternalKeySet = KeySet.ExternalKeySet,
|
ExternalKeySet = KeySet.ExternalKeySet,
|
||||||
FsCreators = fsServerObjects.FsCreators
|
FsCreators = fsServerObjects.FsCreators,
|
||||||
|
RandomGenerator = randomGenerator
|
||||||
};
|
};
|
||||||
|
|
||||||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
||||||
|
@@ -850,7 +850,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
for (int i = 0; i < programCount; i++)
|
for (int i = 0; i < programCount; i++)
|
||||||
{
|
{
|
||||||
mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i);
|
mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i);
|
||||||
mapInfo[i].MainProgramId = new ProgramId(applicationId);
|
mapInfo[i].MainProgramId = new ApplicationId(applicationId);
|
||||||
mapInfo[i].ProgramIndex = (byte)i;
|
mapInfo[i].ProgramIndex = (byte)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -60,8 +60,6 @@ namespace Ryujinx.HLE.HOS
|
|||||||
virtualFileSystem.InitializeFsServer(Server, out var fsClient);
|
virtualFileSystem.InitializeFsServer(Server, out var fsClient);
|
||||||
|
|
||||||
FsClient = fsClient;
|
FsClient = fsClient;
|
||||||
|
|
||||||
CleanSdCardDirectory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeSystemClients()
|
public void InitializeSystemClients()
|
||||||
@@ -78,27 +76,6 @@ namespace Ryujinx.HLE.HOS
|
|||||||
ApplicationClient = Server.CreateHorizonClient(new ProgramLocation(programId, StorageId.BuiltInUser), npdm.FsAccessControlData, npdm.FsAccessControlDescriptor);
|
ApplicationClient = Server.CreateHorizonClient(new ProgramLocation(programId, StorageId.BuiltInUser), npdm.FsAccessControlData, npdm.FsAccessControlDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function was added to avoid errors that come from a user's keys or SD encryption seed changing.
|
|
||||||
// Catching these errors and recreating the file ended up not working because of the different ways
|
|
||||||
// applications respond to a file suddenly containing all zeros or having a length of zero.
|
|
||||||
// Clearing the SD card save directory was determined to be the best option for the moment since
|
|
||||||
// the saves on the SD card are meant as caches that can be deleted at any time.
|
|
||||||
private void CleanSdCardDirectory()
|
|
||||||
{
|
|
||||||
Result rc = RyujinxClient.Fs.MountSdCard("sdcard".ToU8Span());
|
|
||||||
if (rc.IsFailure()) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RyujinxClient.Fs.CleanDirectoryRecursively("sdcard:/Nintendo/save".ToU8Span()).IgnoreResult();
|
|
||||||
RyujinxClient.Fs.DeleteDirectoryRecursively("sdcard:/save".ToU8Span()).IgnoreResult();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
RyujinxClient.Fs.Unmount("sdcard".ToU8Span());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AccessControlBits.Bits AccountFsPermissions => AccessControlBits.Bits.SystemSaveData |
|
private static AccessControlBits.Bits AccountFsPermissions => AccessControlBits.Bits.SystemSaveData |
|
||||||
AccessControlBits.Bits.GameCard |
|
AccessControlBits.Bits.GameCard |
|
||||||
AccessControlBits.Bits.SaveDataMeta |
|
AccessControlBits.Bits.SaveDataMeta |
|
||||||
|
@@ -26,8 +26,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
{
|
{
|
||||||
class IApplicationFunctions : IpcService
|
class IApplicationFunctions : IpcService
|
||||||
{
|
{
|
||||||
private ulong _defaultSaveDataSize = 200000000;
|
private long _defaultSaveDataSize = 200000000;
|
||||||
private ulong _defaultJournalSaveDataSize = 200000000;
|
private long _defaultJournalSaveDataSize = 200000000;
|
||||||
|
|
||||||
private KEvent _gpuErrorDetectedSystemEvent;
|
private KEvent _gpuErrorDetectedSystemEvent;
|
||||||
private KEvent _friendInvitationStorageChannelEvent;
|
private KEvent _friendInvitationStorageChannelEvent;
|
||||||
@@ -203,13 +203,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(25)] // 3.0.0+
|
[CommandHipc(25)] // 3.0.0+
|
||||||
// ExtendSaveData(u8 save_data_type, nn::account::Uid, u64 save_size, u64 journal_size) -> u64 result_code
|
// ExtendSaveData(u8 save_data_type, nn::account::Uid, s64 save_size, s64 journal_size) -> u64 result_code
|
||||||
public ResultCode ExtendSaveData(ServiceCtx context)
|
public ResultCode ExtendSaveData(ServiceCtx context)
|
||||||
{
|
{
|
||||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
|
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
|
||||||
Uid userId = context.RequestData.ReadStruct<Uid>();
|
Uid userId = context.RequestData.ReadStruct<Uid>();
|
||||||
ulong saveDataSize = context.RequestData.ReadUInt64();
|
long saveDataSize = context.RequestData.ReadInt64();
|
||||||
ulong journalSize = context.RequestData.ReadUInt64();
|
long journalSize = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
// NOTE: Service calls nn::fs::ExtendApplicationSaveData.
|
// NOTE: Service calls nn::fs::ExtendApplicationSaveData.
|
||||||
// Since LibHac currently doesn't support this method, we can stub it for now.
|
// Since LibHac currently doesn't support this method, we can stub it for now.
|
||||||
@@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(26)] // 3.0.0+
|
[CommandHipc(26)] // 3.0.0+
|
||||||
// GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (u64 save_size, u64 journal_size)
|
// GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (s64 save_size, s64 journal_size)
|
||||||
public ResultCode GetSaveDataSize(ServiceCtx context)
|
public ResultCode GetSaveDataSize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
|
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
|
||||||
@@ -268,6 +268,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(28)] // 11.0.0+
|
||||||
|
// GetSaveDataSizeMax() -> (s64 save_size_max, s64 journal_size_max)
|
||||||
|
public ResultCode GetSaveDataSizeMax(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// NOTE: We are currently using a stub for GetSaveDataSize() which returns the default values.
|
||||||
|
// For this method we shouldn't return anything lower than that, but since we aren't interacting
|
||||||
|
// with fs to get the actual sizes, we return the default values here as well.
|
||||||
|
// This also helps in case ExtendSaveData() has been executed and the default values were modified.
|
||||||
|
|
||||||
|
context.ResponseData.Write(_defaultSaveDataSize);
|
||||||
|
context.ResponseData.Write(_defaultJournalSaveDataSize);
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(30)]
|
[CommandHipc(30)]
|
||||||
// BeginBlockingHomeButtonShortAndLongPressed()
|
// BeginBlockingHomeButtonShortAndLongPressed()
|
||||||
public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context)
|
public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context)
|
||||||
|
@@ -43,6 +43,16 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(1)]
|
||||||
|
// nn::friends::Cancel()
|
||||||
|
public ResultCode Cancel(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// TODO: Original service sets an internal field to 1 here. Determine usage.
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceFriend);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(10100)]
|
[CommandHipc(10100)]
|
||||||
// nn::friends::GetFriendListIds(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid)
|
// nn::friends::GetFriendListIds(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid)
|
||||||
// -> int outCount, array<nn::account::NetworkServiceAccountId, 0xa>
|
// -> int outCount, array<nn::account::NetworkServiceAccountId, 0xa>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
|
||||||
|
using GameCardHandle = System.UInt32;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Fs
|
namespace Ryujinx.HLE.HOS.Services.Fs
|
||||||
{
|
{
|
||||||
@@ -41,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
{
|
{
|
||||||
Result result = _baseOperator.Get.GetGameCardHandle(out GameCardHandle handle);
|
Result result = _baseOperator.Get.GetGameCardHandle(out GameCardHandle handle);
|
||||||
|
|
||||||
context.ResponseData.Write(handle.Value);
|
context.ResponseData.Write(handle);
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
return (ResultCode)result.Value;
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ using static Ryujinx.HLE.Utilities.StringUtils;
|
|||||||
using IFileSystem = LibHac.FsSrv.Sf.IFileSystem;
|
using IFileSystem = LibHac.FsSrv.Sf.IFileSystem;
|
||||||
using IStorage = LibHac.FsSrv.Sf.IStorage;
|
using IStorage = LibHac.FsSrv.Sf.IStorage;
|
||||||
using RightsId = LibHac.Fs.RightsId;
|
using RightsId = LibHac.Fs.RightsId;
|
||||||
|
using GameCardHandle = System.UInt32;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Fs
|
namespace Ryujinx.HLE.HOS.Services.Fs
|
||||||
{
|
{
|
||||||
@@ -239,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
// OpenGameCardStorage(u32 handle, u32 partitionId) -> object<nn::fssrv::sf::IStorage>
|
// OpenGameCardStorage(u32 handle, u32 partitionId) -> object<nn::fssrv::sf::IStorage>
|
||||||
public ResultCode OpenGameCardStorage(ServiceCtx context)
|
public ResultCode OpenGameCardStorage(ServiceCtx context)
|
||||||
{
|
{
|
||||||
GameCardHandle handle = new GameCardHandle(context.RequestData.ReadInt32());
|
GameCardHandle handle = context.RequestData.ReadUInt32();
|
||||||
GameCardPartitionRaw partitionId = (GameCardPartitionRaw)context.RequestData.ReadInt32();
|
GameCardPartitionRaw partitionId = (GameCardPartitionRaw)context.RequestData.ReadInt32();
|
||||||
using var storage = new SharedRef<IStorage>();
|
using var storage = new SharedRef<IStorage>();
|
||||||
|
|
||||||
@@ -255,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
// OpenGameCardFileSystem(u32 handle, u32 partitionId) -> object<nn::fssrv::sf::IFileSystem>
|
// OpenGameCardFileSystem(u32 handle, u32 partitionId) -> object<nn::fssrv::sf::IFileSystem>
|
||||||
public ResultCode OpenGameCardFileSystem(ServiceCtx context)
|
public ResultCode OpenGameCardFileSystem(ServiceCtx context)
|
||||||
{
|
{
|
||||||
GameCardHandle handle = new GameCardHandle(context.RequestData.ReadInt32());
|
GameCardHandle handle = context.RequestData.ReadUInt32();
|
||||||
GameCardPartition partitionId = (GameCardPartition)context.RequestData.ReadInt32();
|
GameCardPartition partitionId = (GameCardPartition)context.RequestData.ReadInt32();
|
||||||
using var fileSystem = new SharedRef<IFileSystem>();
|
using var fileSystem = new SharedRef<IFileSystem>();
|
||||||
|
|
||||||
@@ -315,6 +316,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaCreateInfo, in hashSalt).Value;
|
return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaCreateInfo, in hashSalt).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(37)] // 14.0.0+
|
||||||
|
// CreateSaveDataFileSystemWithCreationInfo2(buffer<nn::fs::SaveDataCreationInfo2, 25> creationInfo) -> ()
|
||||||
|
public ResultCode CreateSaveDataFileSystemWithCreationInfo2(ServiceCtx context)
|
||||||
|
{
|
||||||
|
byte[] creationInfoBuffer = new byte[context.Request.SendBuff[0].Size];
|
||||||
|
context.Memory.Read(context.Request.SendBuff[0].Position, creationInfoBuffer);
|
||||||
|
ref readonly SaveDataCreationInfo2 creationInfo = ref SpanHelpers.AsReadOnlyStruct<SaveDataCreationInfo2>(creationInfoBuffer);
|
||||||
|
|
||||||
|
return (ResultCode)_baseFileSystemProxy.Get.CreateSaveDataFileSystemWithCreationInfo2(in creationInfo).Value;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandHipc(51)]
|
[CommandHipc(51)]
|
||||||
// OpenSaveDataFileSystem(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> object<nn::fssrv::sf::IFileSystem> saveDataFs
|
// OpenSaveDataFileSystem(u8 spaceId, nn::fs::SaveDataAttribute attribute) -> object<nn::fssrv::sf::IFileSystem> saveDataFs
|
||||||
public ResultCode OpenSaveDataFileSystem(ServiceCtx context)
|
public ResultCode OpenSaveDataFileSystem(ServiceCtx context)
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Concentus" Version="1.1.7" />
|
<PackageReference Include="Concentus" Version="1.1.7" />
|
||||||
<PackageReference Include="LibHac" Version="0.16.1" />
|
<PackageReference Include="LibHac" Version="0.17.0" />
|
||||||
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
|
@@ -25,6 +25,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
private int _sequenceNumber;
|
private int _sequenceNumber;
|
||||||
private BitMap _sequenceNumberBitmap;
|
private BitMap _sequenceNumberBitmap;
|
||||||
|
private BitMap _dirtyCheckedBitmap;
|
||||||
private int _uncheckedHandles;
|
private int _uncheckedHandles;
|
||||||
|
|
||||||
public bool Dirty { get; private set; } = true;
|
public bool Dirty { get; private set; } = true;
|
||||||
@@ -36,6 +37,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
_dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
|
_dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
|
||||||
_sequenceNumberBitmap = new BitMap(_handles.Length);
|
_sequenceNumberBitmap = new BitMap(_handles.Length);
|
||||||
|
_dirtyCheckedBitmap = new BitMap(_handles.Length);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@@ -246,16 +248,18 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
|
private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, long[] checkMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
|
||||||
{
|
{
|
||||||
long seqMask = mask & ~seqMasks[index];
|
long seqMask = mask & ~seqMasks[index];
|
||||||
|
long checkMask = (~dirtyBits) & seqMask;
|
||||||
dirtyBits &= seqMask;
|
dirtyBits &= seqMask;
|
||||||
|
|
||||||
while (dirtyBits != 0)
|
while (dirtyBits != 0)
|
||||||
{
|
{
|
||||||
int bit = BitOperations.TrailingZeroCount(dirtyBits);
|
int bit = BitOperations.TrailingZeroCount(dirtyBits);
|
||||||
|
long bitValue = 1L << bit;
|
||||||
|
|
||||||
dirtyBits &= ~(1L << bit);
|
dirtyBits &= ~bitValue;
|
||||||
|
|
||||||
int handleIndex = baseBit + bit;
|
int handleIndex = baseBit + bit;
|
||||||
|
|
||||||
@@ -273,11 +277,14 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
|
|
||||||
rgSize += handle.Size;
|
rgSize += handle.Size;
|
||||||
handle.Reprotect();
|
handle.Reprotect(false, (checkMasks[index] & bitValue) == 0);
|
||||||
|
|
||||||
|
checkMasks[index] &= ~bitValue;
|
||||||
|
|
||||||
prevHandle = handleIndex;
|
prevHandle = handleIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkMasks[index] |= checkMask;
|
||||||
seqMasks[index] |= mask;
|
seqMasks[index] |= mask;
|
||||||
_uncheckedHandles -= BitOperations.PopCount((ulong)seqMask);
|
_uncheckedHandles -= BitOperations.PopCount((ulong)seqMask);
|
||||||
|
|
||||||
@@ -328,6 +335,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
ulong rgSize = 0;
|
ulong rgSize = 0;
|
||||||
|
|
||||||
long[] seqMasks = _sequenceNumberBitmap.Masks;
|
long[] seqMasks = _sequenceNumberBitmap.Masks;
|
||||||
|
long[] checkedMasks = _dirtyCheckedBitmap.Masks;
|
||||||
long[] masks = _dirtyBitmap.Masks;
|
long[] masks = _dirtyBitmap.Masks;
|
||||||
|
|
||||||
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
|
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
|
||||||
@@ -345,20 +353,20 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
if (startIndex == endIndex)
|
if (startIndex == endIndex)
|
||||||
{
|
{
|
||||||
ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
ParseDirtyBits(startValue, startMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
|
||||||
for (int i = startIndex + 1; i < endIndex; i++)
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
{
|
{
|
||||||
ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
long endValue = Volatile.Read(ref masks[endIndex]);
|
long endValue = Volatile.Read(ref masks[endIndex]);
|
||||||
|
|
||||||
ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
ParseDirtyBits(endValue, endMask, endIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rgSize != 0)
|
if (rgSize != 0)
|
||||||
|
@@ -263,15 +263,15 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ForceDirty()
|
public void ForceDirty()
|
||||||
{
|
{
|
||||||
_checkCount++;
|
|
||||||
|
|
||||||
Dirty = true;
|
Dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
|
/// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Reprotect(bool asDirty = false)
|
/// <param name="asDirty">True if the handle should be reprotected as dirty, rather than have it cleared</param>
|
||||||
|
/// <param name="consecutiveCheck">True if this reprotect is the result of consecutive dirty checks</param>
|
||||||
|
public void Reprotect(bool asDirty, bool consecutiveCheck = false)
|
||||||
{
|
{
|
||||||
if (_volatile) return;
|
if (_volatile) return;
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
else if (!asDirty)
|
else if (!asDirty)
|
||||||
{
|
{
|
||||||
if (_checkCount > 0 && _checkCount < CheckCountForInfrequent)
|
if (consecutiveCheck || (_checkCount > 0 && _checkCount < CheckCountForInfrequent))
|
||||||
{
|
{
|
||||||
if (++_volatileCount >= VolatileThreshold && _preAction == null)
|
if (++_volatileCount >= VolatileThreshold && _preAction == null)
|
||||||
{
|
{
|
||||||
@@ -313,6 +313,15 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asDirty">True if the handle should be reprotected as dirty, rather than have it cleared</param>
|
||||||
|
public void Reprotect(bool asDirty = false)
|
||||||
|
{
|
||||||
|
Reprotect(asDirty, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register an action to perform when the tracked region is read or written.
|
/// Register an action to perform when the tracked region is read or written.
|
||||||
/// The action is automatically removed after it runs.
|
/// The action is automatically removed after it runs.
|
||||||
|
@@ -1,135 +0,0 @@
|
|||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
public enum Arm32Register
|
|
||||||
{
|
|
||||||
INVALID = 0,
|
|
||||||
|
|
||||||
APSR,
|
|
||||||
APSR_NZCV,
|
|
||||||
CPSR,
|
|
||||||
FPEXC,
|
|
||||||
FPINST,
|
|
||||||
FPSCR,
|
|
||||||
FPSCR_NZCV,
|
|
||||||
FPSID,
|
|
||||||
ITSTATE,
|
|
||||||
LR,
|
|
||||||
PC,
|
|
||||||
SP,
|
|
||||||
SPSR,
|
|
||||||
D0,
|
|
||||||
D1,
|
|
||||||
D2,
|
|
||||||
D3,
|
|
||||||
D4,
|
|
||||||
D5,
|
|
||||||
D6,
|
|
||||||
D7,
|
|
||||||
D8,
|
|
||||||
D9,
|
|
||||||
D10,
|
|
||||||
D11,
|
|
||||||
D12,
|
|
||||||
D13,
|
|
||||||
D14,
|
|
||||||
D15,
|
|
||||||
D16,
|
|
||||||
D17,
|
|
||||||
D18,
|
|
||||||
D19,
|
|
||||||
D20,
|
|
||||||
D21,
|
|
||||||
D22,
|
|
||||||
D23,
|
|
||||||
D24,
|
|
||||||
D25,
|
|
||||||
D26,
|
|
||||||
D27,
|
|
||||||
D28,
|
|
||||||
D29,
|
|
||||||
D30,
|
|
||||||
D31,
|
|
||||||
FPINST2,
|
|
||||||
MVFR0,
|
|
||||||
MVFR1,
|
|
||||||
MVFR2,
|
|
||||||
Q0,
|
|
||||||
Q1,
|
|
||||||
Q2,
|
|
||||||
Q3,
|
|
||||||
Q4,
|
|
||||||
Q5,
|
|
||||||
Q6,
|
|
||||||
Q7,
|
|
||||||
Q8,
|
|
||||||
Q9,
|
|
||||||
Q10,
|
|
||||||
Q11,
|
|
||||||
Q12,
|
|
||||||
Q13,
|
|
||||||
Q14,
|
|
||||||
Q15,
|
|
||||||
R0,
|
|
||||||
R1,
|
|
||||||
R2,
|
|
||||||
R3,
|
|
||||||
R4,
|
|
||||||
R5,
|
|
||||||
R6,
|
|
||||||
R7,
|
|
||||||
R8,
|
|
||||||
R9,
|
|
||||||
R10,
|
|
||||||
R11,
|
|
||||||
R12,
|
|
||||||
S0,
|
|
||||||
S1,
|
|
||||||
S2,
|
|
||||||
S3,
|
|
||||||
S4,
|
|
||||||
S5,
|
|
||||||
S6,
|
|
||||||
S7,
|
|
||||||
S8,
|
|
||||||
S9,
|
|
||||||
S10,
|
|
||||||
S11,
|
|
||||||
S12,
|
|
||||||
S13,
|
|
||||||
S14,
|
|
||||||
S15,
|
|
||||||
S16,
|
|
||||||
S17,
|
|
||||||
S18,
|
|
||||||
S19,
|
|
||||||
S20,
|
|
||||||
S21,
|
|
||||||
S22,
|
|
||||||
S23,
|
|
||||||
S24,
|
|
||||||
S25,
|
|
||||||
S26,
|
|
||||||
S27,
|
|
||||||
S28,
|
|
||||||
S29,
|
|
||||||
S30,
|
|
||||||
S31,
|
|
||||||
C1_C0_2,
|
|
||||||
C13_C0_2,
|
|
||||||
C13_C0_3,
|
|
||||||
IPSR,
|
|
||||||
MSP,
|
|
||||||
PSP,
|
|
||||||
CONTROL,
|
|
||||||
ENDING,
|
|
||||||
|
|
||||||
// Alias registers.
|
|
||||||
R13 = SP,
|
|
||||||
R14 = LR,
|
|
||||||
R15 = PC,
|
|
||||||
SB = R9,
|
|
||||||
SL = R10,
|
|
||||||
FP = R11,
|
|
||||||
IP = R12,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,295 +0,0 @@
|
|||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
public enum ArmRegister
|
|
||||||
{
|
|
||||||
INVALID = 0,
|
|
||||||
|
|
||||||
X29,
|
|
||||||
X30,
|
|
||||||
NZCV,
|
|
||||||
SP,
|
|
||||||
WSP,
|
|
||||||
WZR,
|
|
||||||
XZR,
|
|
||||||
B0,
|
|
||||||
B1,
|
|
||||||
B2,
|
|
||||||
B3,
|
|
||||||
B4,
|
|
||||||
B5,
|
|
||||||
B6,
|
|
||||||
B7,
|
|
||||||
B8,
|
|
||||||
B9,
|
|
||||||
B10,
|
|
||||||
B11,
|
|
||||||
B12,
|
|
||||||
B13,
|
|
||||||
B14,
|
|
||||||
B15,
|
|
||||||
B16,
|
|
||||||
B17,
|
|
||||||
B18,
|
|
||||||
B19,
|
|
||||||
B20,
|
|
||||||
B21,
|
|
||||||
B22,
|
|
||||||
B23,
|
|
||||||
B24,
|
|
||||||
B25,
|
|
||||||
B26,
|
|
||||||
B27,
|
|
||||||
B28,
|
|
||||||
B29,
|
|
||||||
B30,
|
|
||||||
B31,
|
|
||||||
D0,
|
|
||||||
D1,
|
|
||||||
D2,
|
|
||||||
D3,
|
|
||||||
D4,
|
|
||||||
D5,
|
|
||||||
D6,
|
|
||||||
D7,
|
|
||||||
D8,
|
|
||||||
D9,
|
|
||||||
D10,
|
|
||||||
D11,
|
|
||||||
D12,
|
|
||||||
D13,
|
|
||||||
D14,
|
|
||||||
D15,
|
|
||||||
D16,
|
|
||||||
D17,
|
|
||||||
D18,
|
|
||||||
D19,
|
|
||||||
D20,
|
|
||||||
D21,
|
|
||||||
D22,
|
|
||||||
D23,
|
|
||||||
D24,
|
|
||||||
D25,
|
|
||||||
D26,
|
|
||||||
D27,
|
|
||||||
D28,
|
|
||||||
D29,
|
|
||||||
D30,
|
|
||||||
D31,
|
|
||||||
H0,
|
|
||||||
H1,
|
|
||||||
H2,
|
|
||||||
H3,
|
|
||||||
H4,
|
|
||||||
H5,
|
|
||||||
H6,
|
|
||||||
H7,
|
|
||||||
H8,
|
|
||||||
H9,
|
|
||||||
H10,
|
|
||||||
H11,
|
|
||||||
H12,
|
|
||||||
H13,
|
|
||||||
H14,
|
|
||||||
H15,
|
|
||||||
H16,
|
|
||||||
H17,
|
|
||||||
H18,
|
|
||||||
H19,
|
|
||||||
H20,
|
|
||||||
H21,
|
|
||||||
H22,
|
|
||||||
H23,
|
|
||||||
H24,
|
|
||||||
H25,
|
|
||||||
H26,
|
|
||||||
H27,
|
|
||||||
H28,
|
|
||||||
H29,
|
|
||||||
H30,
|
|
||||||
H31,
|
|
||||||
Q0,
|
|
||||||
Q1,
|
|
||||||
Q2,
|
|
||||||
Q3,
|
|
||||||
Q4,
|
|
||||||
Q5,
|
|
||||||
Q6,
|
|
||||||
Q7,
|
|
||||||
Q8,
|
|
||||||
Q9,
|
|
||||||
Q10,
|
|
||||||
Q11,
|
|
||||||
Q12,
|
|
||||||
Q13,
|
|
||||||
Q14,
|
|
||||||
Q15,
|
|
||||||
Q16,
|
|
||||||
Q17,
|
|
||||||
Q18,
|
|
||||||
Q19,
|
|
||||||
Q20,
|
|
||||||
Q21,
|
|
||||||
Q22,
|
|
||||||
Q23,
|
|
||||||
Q24,
|
|
||||||
Q25,
|
|
||||||
Q26,
|
|
||||||
Q27,
|
|
||||||
Q28,
|
|
||||||
Q29,
|
|
||||||
Q30,
|
|
||||||
Q31,
|
|
||||||
S0,
|
|
||||||
S1,
|
|
||||||
S2,
|
|
||||||
S3,
|
|
||||||
S4,
|
|
||||||
S5,
|
|
||||||
S6,
|
|
||||||
S7,
|
|
||||||
S8,
|
|
||||||
S9,
|
|
||||||
S10,
|
|
||||||
S11,
|
|
||||||
S12,
|
|
||||||
S13,
|
|
||||||
S14,
|
|
||||||
S15,
|
|
||||||
S16,
|
|
||||||
S17,
|
|
||||||
S18,
|
|
||||||
S19,
|
|
||||||
S20,
|
|
||||||
S21,
|
|
||||||
S22,
|
|
||||||
S23,
|
|
||||||
S24,
|
|
||||||
S25,
|
|
||||||
S26,
|
|
||||||
S27,
|
|
||||||
S28,
|
|
||||||
S29,
|
|
||||||
S30,
|
|
||||||
S31,
|
|
||||||
W0,
|
|
||||||
W1,
|
|
||||||
W2,
|
|
||||||
W3,
|
|
||||||
W4,
|
|
||||||
W5,
|
|
||||||
W6,
|
|
||||||
W7,
|
|
||||||
W8,
|
|
||||||
W9,
|
|
||||||
W10,
|
|
||||||
W11,
|
|
||||||
W12,
|
|
||||||
W13,
|
|
||||||
W14,
|
|
||||||
W15,
|
|
||||||
W16,
|
|
||||||
W17,
|
|
||||||
W18,
|
|
||||||
W19,
|
|
||||||
W20,
|
|
||||||
W21,
|
|
||||||
W22,
|
|
||||||
W23,
|
|
||||||
W24,
|
|
||||||
W25,
|
|
||||||
W26,
|
|
||||||
W27,
|
|
||||||
W28,
|
|
||||||
W29,
|
|
||||||
W30,
|
|
||||||
X0,
|
|
||||||
X1,
|
|
||||||
X2,
|
|
||||||
X3,
|
|
||||||
X4,
|
|
||||||
X5,
|
|
||||||
X6,
|
|
||||||
X7,
|
|
||||||
X8,
|
|
||||||
X9,
|
|
||||||
X10,
|
|
||||||
X11,
|
|
||||||
X12,
|
|
||||||
X13,
|
|
||||||
X14,
|
|
||||||
X15,
|
|
||||||
X16,
|
|
||||||
X17,
|
|
||||||
X18,
|
|
||||||
X19,
|
|
||||||
X20,
|
|
||||||
X21,
|
|
||||||
X22,
|
|
||||||
X23,
|
|
||||||
X24,
|
|
||||||
X25,
|
|
||||||
X26,
|
|
||||||
X27,
|
|
||||||
X28,
|
|
||||||
|
|
||||||
V0,
|
|
||||||
V1,
|
|
||||||
V2,
|
|
||||||
V3,
|
|
||||||
V4,
|
|
||||||
V5,
|
|
||||||
V6,
|
|
||||||
V7,
|
|
||||||
V8,
|
|
||||||
V9,
|
|
||||||
V10,
|
|
||||||
V11,
|
|
||||||
V12,
|
|
||||||
V13,
|
|
||||||
V14,
|
|
||||||
V15,
|
|
||||||
V16,
|
|
||||||
V17,
|
|
||||||
V18,
|
|
||||||
V19,
|
|
||||||
V20,
|
|
||||||
V21,
|
|
||||||
V22,
|
|
||||||
V23,
|
|
||||||
V24,
|
|
||||||
V25,
|
|
||||||
V26,
|
|
||||||
V27,
|
|
||||||
V28,
|
|
||||||
V29,
|
|
||||||
V30,
|
|
||||||
V31,
|
|
||||||
|
|
||||||
// > pseudo registers
|
|
||||||
PC, // program counter register
|
|
||||||
|
|
||||||
CPACR_EL1,
|
|
||||||
ESR,
|
|
||||||
|
|
||||||
// > thread registers
|
|
||||||
TPIDR_EL0,
|
|
||||||
TPIDRRO_EL0,
|
|
||||||
TPIDR_EL1,
|
|
||||||
|
|
||||||
PSTATE, // PSTATE pseudoregister
|
|
||||||
|
|
||||||
// > floating point control and status registers
|
|
||||||
FPCR,
|
|
||||||
FPSR,
|
|
||||||
|
|
||||||
ENDING, // <-- mark the end of the list of registers
|
|
||||||
|
|
||||||
// > alias registers
|
|
||||||
|
|
||||||
IP0 = X16,
|
|
||||||
IP1 = X17,
|
|
||||||
FP = X29,
|
|
||||||
LR = X30,
|
|
||||||
}
|
|
||||||
}
|
|
20
Ryujinx.Tests.Unicorn/Native/Const/Arch.cs
Normal file
20
Ryujinx.Tests.Unicorn/Native/Const/Arch.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Arch
|
||||||
|
{
|
||||||
|
ARM = 1,
|
||||||
|
ARM64 = 2,
|
||||||
|
MIPS = 3,
|
||||||
|
X86 = 4,
|
||||||
|
PPC = 5,
|
||||||
|
SPARC = 6,
|
||||||
|
M68K = 7,
|
||||||
|
RISCV = 8,
|
||||||
|
S390X = 9,
|
||||||
|
TRICORE = 10,
|
||||||
|
MAX = 11,
|
||||||
|
}
|
||||||
|
}
|
200
Ryujinx.Tests.Unicorn/Native/Const/Arm.cs
Normal file
200
Ryujinx.Tests.Unicorn/Native/Const/Arm.cs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Arm
|
||||||
|
{
|
||||||
|
|
||||||
|
// ARM CPU
|
||||||
|
|
||||||
|
CPU_ARM_926 = 0,
|
||||||
|
CPU_ARM_946 = 1,
|
||||||
|
CPU_ARM_1026 = 2,
|
||||||
|
CPU_ARM_1136_R2 = 3,
|
||||||
|
CPU_ARM_1136 = 4,
|
||||||
|
CPU_ARM_1176 = 5,
|
||||||
|
CPU_ARM_11MPCORE = 6,
|
||||||
|
CPU_ARM_CORTEX_M0 = 7,
|
||||||
|
CPU_ARM_CORTEX_M3 = 8,
|
||||||
|
CPU_ARM_CORTEX_M4 = 9,
|
||||||
|
CPU_ARM_CORTEX_M7 = 10,
|
||||||
|
CPU_ARM_CORTEX_M33 = 11,
|
||||||
|
CPU_ARM_CORTEX_R5 = 12,
|
||||||
|
CPU_ARM_CORTEX_R5F = 13,
|
||||||
|
CPU_ARM_CORTEX_A7 = 14,
|
||||||
|
CPU_ARM_CORTEX_A8 = 15,
|
||||||
|
CPU_ARM_CORTEX_A9 = 16,
|
||||||
|
CPU_ARM_CORTEX_A15 = 17,
|
||||||
|
CPU_ARM_TI925T = 18,
|
||||||
|
CPU_ARM_SA1100 = 19,
|
||||||
|
CPU_ARM_SA1110 = 20,
|
||||||
|
CPU_ARM_PXA250 = 21,
|
||||||
|
CPU_ARM_PXA255 = 22,
|
||||||
|
CPU_ARM_PXA260 = 23,
|
||||||
|
CPU_ARM_PXA261 = 24,
|
||||||
|
CPU_ARM_PXA262 = 25,
|
||||||
|
CPU_ARM_PXA270 = 26,
|
||||||
|
CPU_ARM_PXA270A0 = 27,
|
||||||
|
CPU_ARM_PXA270A1 = 28,
|
||||||
|
CPU_ARM_PXA270B0 = 29,
|
||||||
|
CPU_ARM_PXA270B1 = 30,
|
||||||
|
CPU_ARM_PXA270C0 = 31,
|
||||||
|
CPU_ARM_PXA270C5 = 32,
|
||||||
|
CPU_ARM_MAX = 33,
|
||||||
|
CPU_ARM_ENDING = 34,
|
||||||
|
|
||||||
|
// ARM registers
|
||||||
|
|
||||||
|
REG_INVALID = 0,
|
||||||
|
REG_APSR = 1,
|
||||||
|
REG_APSR_NZCV = 2,
|
||||||
|
REG_CPSR = 3,
|
||||||
|
REG_FPEXC = 4,
|
||||||
|
REG_FPINST = 5,
|
||||||
|
REG_FPSCR = 6,
|
||||||
|
REG_FPSCR_NZCV = 7,
|
||||||
|
REG_FPSID = 8,
|
||||||
|
REG_ITSTATE = 9,
|
||||||
|
REG_LR = 10,
|
||||||
|
REG_PC = 11,
|
||||||
|
REG_SP = 12,
|
||||||
|
REG_SPSR = 13,
|
||||||
|
REG_D0 = 14,
|
||||||
|
REG_D1 = 15,
|
||||||
|
REG_D2 = 16,
|
||||||
|
REG_D3 = 17,
|
||||||
|
REG_D4 = 18,
|
||||||
|
REG_D5 = 19,
|
||||||
|
REG_D6 = 20,
|
||||||
|
REG_D7 = 21,
|
||||||
|
REG_D8 = 22,
|
||||||
|
REG_D9 = 23,
|
||||||
|
REG_D10 = 24,
|
||||||
|
REG_D11 = 25,
|
||||||
|
REG_D12 = 26,
|
||||||
|
REG_D13 = 27,
|
||||||
|
REG_D14 = 28,
|
||||||
|
REG_D15 = 29,
|
||||||
|
REG_D16 = 30,
|
||||||
|
REG_D17 = 31,
|
||||||
|
REG_D18 = 32,
|
||||||
|
REG_D19 = 33,
|
||||||
|
REG_D20 = 34,
|
||||||
|
REG_D21 = 35,
|
||||||
|
REG_D22 = 36,
|
||||||
|
REG_D23 = 37,
|
||||||
|
REG_D24 = 38,
|
||||||
|
REG_D25 = 39,
|
||||||
|
REG_D26 = 40,
|
||||||
|
REG_D27 = 41,
|
||||||
|
REG_D28 = 42,
|
||||||
|
REG_D29 = 43,
|
||||||
|
REG_D30 = 44,
|
||||||
|
REG_D31 = 45,
|
||||||
|
REG_FPINST2 = 46,
|
||||||
|
REG_MVFR0 = 47,
|
||||||
|
REG_MVFR1 = 48,
|
||||||
|
REG_MVFR2 = 49,
|
||||||
|
REG_Q0 = 50,
|
||||||
|
REG_Q1 = 51,
|
||||||
|
REG_Q2 = 52,
|
||||||
|
REG_Q3 = 53,
|
||||||
|
REG_Q4 = 54,
|
||||||
|
REG_Q5 = 55,
|
||||||
|
REG_Q6 = 56,
|
||||||
|
REG_Q7 = 57,
|
||||||
|
REG_Q8 = 58,
|
||||||
|
REG_Q9 = 59,
|
||||||
|
REG_Q10 = 60,
|
||||||
|
REG_Q11 = 61,
|
||||||
|
REG_Q12 = 62,
|
||||||
|
REG_Q13 = 63,
|
||||||
|
REG_Q14 = 64,
|
||||||
|
REG_Q15 = 65,
|
||||||
|
REG_R0 = 66,
|
||||||
|
REG_R1 = 67,
|
||||||
|
REG_R2 = 68,
|
||||||
|
REG_R3 = 69,
|
||||||
|
REG_R4 = 70,
|
||||||
|
REG_R5 = 71,
|
||||||
|
REG_R6 = 72,
|
||||||
|
REG_R7 = 73,
|
||||||
|
REG_R8 = 74,
|
||||||
|
REG_R9 = 75,
|
||||||
|
REG_R10 = 76,
|
||||||
|
REG_R11 = 77,
|
||||||
|
REG_R12 = 78,
|
||||||
|
REG_S0 = 79,
|
||||||
|
REG_S1 = 80,
|
||||||
|
REG_S2 = 81,
|
||||||
|
REG_S3 = 82,
|
||||||
|
REG_S4 = 83,
|
||||||
|
REG_S5 = 84,
|
||||||
|
REG_S6 = 85,
|
||||||
|
REG_S7 = 86,
|
||||||
|
REG_S8 = 87,
|
||||||
|
REG_S9 = 88,
|
||||||
|
REG_S10 = 89,
|
||||||
|
REG_S11 = 90,
|
||||||
|
REG_S12 = 91,
|
||||||
|
REG_S13 = 92,
|
||||||
|
REG_S14 = 93,
|
||||||
|
REG_S15 = 94,
|
||||||
|
REG_S16 = 95,
|
||||||
|
REG_S17 = 96,
|
||||||
|
REG_S18 = 97,
|
||||||
|
REG_S19 = 98,
|
||||||
|
REG_S20 = 99,
|
||||||
|
REG_S21 = 100,
|
||||||
|
REG_S22 = 101,
|
||||||
|
REG_S23 = 102,
|
||||||
|
REG_S24 = 103,
|
||||||
|
REG_S25 = 104,
|
||||||
|
REG_S26 = 105,
|
||||||
|
REG_S27 = 106,
|
||||||
|
REG_S28 = 107,
|
||||||
|
REG_S29 = 108,
|
||||||
|
REG_S30 = 109,
|
||||||
|
REG_S31 = 110,
|
||||||
|
REG_C1_C0_2 = 111,
|
||||||
|
REG_C13_C0_2 = 112,
|
||||||
|
REG_C13_C0_3 = 113,
|
||||||
|
REG_IPSR = 114,
|
||||||
|
REG_MSP = 115,
|
||||||
|
REG_PSP = 116,
|
||||||
|
REG_CONTROL = 117,
|
||||||
|
REG_IAPSR = 118,
|
||||||
|
REG_EAPSR = 119,
|
||||||
|
REG_XPSR = 120,
|
||||||
|
REG_EPSR = 121,
|
||||||
|
REG_IEPSR = 122,
|
||||||
|
REG_PRIMASK = 123,
|
||||||
|
REG_BASEPRI = 124,
|
||||||
|
REG_BASEPRI_MAX = 125,
|
||||||
|
REG_FAULTMASK = 126,
|
||||||
|
REG_APSR_NZCVQ = 127,
|
||||||
|
REG_APSR_G = 128,
|
||||||
|
REG_APSR_NZCVQG = 129,
|
||||||
|
REG_IAPSR_NZCVQ = 130,
|
||||||
|
REG_IAPSR_G = 131,
|
||||||
|
REG_IAPSR_NZCVQG = 132,
|
||||||
|
REG_EAPSR_NZCVQ = 133,
|
||||||
|
REG_EAPSR_G = 134,
|
||||||
|
REG_EAPSR_NZCVQG = 135,
|
||||||
|
REG_XPSR_NZCVQ = 136,
|
||||||
|
REG_XPSR_G = 137,
|
||||||
|
REG_XPSR_NZCVQG = 138,
|
||||||
|
REG_CP_REG = 139,
|
||||||
|
REG_ENDING = 140,
|
||||||
|
|
||||||
|
// alias registers
|
||||||
|
REG_R13 = 12,
|
||||||
|
REG_R14 = 10,
|
||||||
|
REG_R15 = 11,
|
||||||
|
REG_SB = 75,
|
||||||
|
REG_SL = 76,
|
||||||
|
REG_FP = 77,
|
||||||
|
REG_IP = 78,
|
||||||
|
}
|
||||||
|
}
|
341
Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs
Normal file
341
Ryujinx.Tests.Unicorn/Native/Const/Arm64.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Arm64
|
||||||
|
{
|
||||||
|
|
||||||
|
// ARM64 CPU
|
||||||
|
|
||||||
|
CPU_ARM64_A57 = 0,
|
||||||
|
CPU_ARM64_A53 = 1,
|
||||||
|
CPU_ARM64_A72 = 2,
|
||||||
|
CPU_ARM64_MAX = 3,
|
||||||
|
CPU_ARM64_ENDING = 4,
|
||||||
|
|
||||||
|
// ARM64 registers
|
||||||
|
|
||||||
|
REG_INVALID = 0,
|
||||||
|
REG_X29 = 1,
|
||||||
|
REG_X30 = 2,
|
||||||
|
REG_NZCV = 3,
|
||||||
|
REG_SP = 4,
|
||||||
|
REG_WSP = 5,
|
||||||
|
REG_WZR = 6,
|
||||||
|
REG_XZR = 7,
|
||||||
|
REG_B0 = 8,
|
||||||
|
REG_B1 = 9,
|
||||||
|
REG_B2 = 10,
|
||||||
|
REG_B3 = 11,
|
||||||
|
REG_B4 = 12,
|
||||||
|
REG_B5 = 13,
|
||||||
|
REG_B6 = 14,
|
||||||
|
REG_B7 = 15,
|
||||||
|
REG_B8 = 16,
|
||||||
|
REG_B9 = 17,
|
||||||
|
REG_B10 = 18,
|
||||||
|
REG_B11 = 19,
|
||||||
|
REG_B12 = 20,
|
||||||
|
REG_B13 = 21,
|
||||||
|
REG_B14 = 22,
|
||||||
|
REG_B15 = 23,
|
||||||
|
REG_B16 = 24,
|
||||||
|
REG_B17 = 25,
|
||||||
|
REG_B18 = 26,
|
||||||
|
REG_B19 = 27,
|
||||||
|
REG_B20 = 28,
|
||||||
|
REG_B21 = 29,
|
||||||
|
REG_B22 = 30,
|
||||||
|
REG_B23 = 31,
|
||||||
|
REG_B24 = 32,
|
||||||
|
REG_B25 = 33,
|
||||||
|
REG_B26 = 34,
|
||||||
|
REG_B27 = 35,
|
||||||
|
REG_B28 = 36,
|
||||||
|
REG_B29 = 37,
|
||||||
|
REG_B30 = 38,
|
||||||
|
REG_B31 = 39,
|
||||||
|
REG_D0 = 40,
|
||||||
|
REG_D1 = 41,
|
||||||
|
REG_D2 = 42,
|
||||||
|
REG_D3 = 43,
|
||||||
|
REG_D4 = 44,
|
||||||
|
REG_D5 = 45,
|
||||||
|
REG_D6 = 46,
|
||||||
|
REG_D7 = 47,
|
||||||
|
REG_D8 = 48,
|
||||||
|
REG_D9 = 49,
|
||||||
|
REG_D10 = 50,
|
||||||
|
REG_D11 = 51,
|
||||||
|
REG_D12 = 52,
|
||||||
|
REG_D13 = 53,
|
||||||
|
REG_D14 = 54,
|
||||||
|
REG_D15 = 55,
|
||||||
|
REG_D16 = 56,
|
||||||
|
REG_D17 = 57,
|
||||||
|
REG_D18 = 58,
|
||||||
|
REG_D19 = 59,
|
||||||
|
REG_D20 = 60,
|
||||||
|
REG_D21 = 61,
|
||||||
|
REG_D22 = 62,
|
||||||
|
REG_D23 = 63,
|
||||||
|
REG_D24 = 64,
|
||||||
|
REG_D25 = 65,
|
||||||
|
REG_D26 = 66,
|
||||||
|
REG_D27 = 67,
|
||||||
|
REG_D28 = 68,
|
||||||
|
REG_D29 = 69,
|
||||||
|
REG_D30 = 70,
|
||||||
|
REG_D31 = 71,
|
||||||
|
REG_H0 = 72,
|
||||||
|
REG_H1 = 73,
|
||||||
|
REG_H2 = 74,
|
||||||
|
REG_H3 = 75,
|
||||||
|
REG_H4 = 76,
|
||||||
|
REG_H5 = 77,
|
||||||
|
REG_H6 = 78,
|
||||||
|
REG_H7 = 79,
|
||||||
|
REG_H8 = 80,
|
||||||
|
REG_H9 = 81,
|
||||||
|
REG_H10 = 82,
|
||||||
|
REG_H11 = 83,
|
||||||
|
REG_H12 = 84,
|
||||||
|
REG_H13 = 85,
|
||||||
|
REG_H14 = 86,
|
||||||
|
REG_H15 = 87,
|
||||||
|
REG_H16 = 88,
|
||||||
|
REG_H17 = 89,
|
||||||
|
REG_H18 = 90,
|
||||||
|
REG_H19 = 91,
|
||||||
|
REG_H20 = 92,
|
||||||
|
REG_H21 = 93,
|
||||||
|
REG_H22 = 94,
|
||||||
|
REG_H23 = 95,
|
||||||
|
REG_H24 = 96,
|
||||||
|
REG_H25 = 97,
|
||||||
|
REG_H26 = 98,
|
||||||
|
REG_H27 = 99,
|
||||||
|
REG_H28 = 100,
|
||||||
|
REG_H29 = 101,
|
||||||
|
REG_H30 = 102,
|
||||||
|
REG_H31 = 103,
|
||||||
|
REG_Q0 = 104,
|
||||||
|
REG_Q1 = 105,
|
||||||
|
REG_Q2 = 106,
|
||||||
|
REG_Q3 = 107,
|
||||||
|
REG_Q4 = 108,
|
||||||
|
REG_Q5 = 109,
|
||||||
|
REG_Q6 = 110,
|
||||||
|
REG_Q7 = 111,
|
||||||
|
REG_Q8 = 112,
|
||||||
|
REG_Q9 = 113,
|
||||||
|
REG_Q10 = 114,
|
||||||
|
REG_Q11 = 115,
|
||||||
|
REG_Q12 = 116,
|
||||||
|
REG_Q13 = 117,
|
||||||
|
REG_Q14 = 118,
|
||||||
|
REG_Q15 = 119,
|
||||||
|
REG_Q16 = 120,
|
||||||
|
REG_Q17 = 121,
|
||||||
|
REG_Q18 = 122,
|
||||||
|
REG_Q19 = 123,
|
||||||
|
REG_Q20 = 124,
|
||||||
|
REG_Q21 = 125,
|
||||||
|
REG_Q22 = 126,
|
||||||
|
REG_Q23 = 127,
|
||||||
|
REG_Q24 = 128,
|
||||||
|
REG_Q25 = 129,
|
||||||
|
REG_Q26 = 130,
|
||||||
|
REG_Q27 = 131,
|
||||||
|
REG_Q28 = 132,
|
||||||
|
REG_Q29 = 133,
|
||||||
|
REG_Q30 = 134,
|
||||||
|
REG_Q31 = 135,
|
||||||
|
REG_S0 = 136,
|
||||||
|
REG_S1 = 137,
|
||||||
|
REG_S2 = 138,
|
||||||
|
REG_S3 = 139,
|
||||||
|
REG_S4 = 140,
|
||||||
|
REG_S5 = 141,
|
||||||
|
REG_S6 = 142,
|
||||||
|
REG_S7 = 143,
|
||||||
|
REG_S8 = 144,
|
||||||
|
REG_S9 = 145,
|
||||||
|
REG_S10 = 146,
|
||||||
|
REG_S11 = 147,
|
||||||
|
REG_S12 = 148,
|
||||||
|
REG_S13 = 149,
|
||||||
|
REG_S14 = 150,
|
||||||
|
REG_S15 = 151,
|
||||||
|
REG_S16 = 152,
|
||||||
|
REG_S17 = 153,
|
||||||
|
REG_S18 = 154,
|
||||||
|
REG_S19 = 155,
|
||||||
|
REG_S20 = 156,
|
||||||
|
REG_S21 = 157,
|
||||||
|
REG_S22 = 158,
|
||||||
|
REG_S23 = 159,
|
||||||
|
REG_S24 = 160,
|
||||||
|
REG_S25 = 161,
|
||||||
|
REG_S26 = 162,
|
||||||
|
REG_S27 = 163,
|
||||||
|
REG_S28 = 164,
|
||||||
|
REG_S29 = 165,
|
||||||
|
REG_S30 = 166,
|
||||||
|
REG_S31 = 167,
|
||||||
|
REG_W0 = 168,
|
||||||
|
REG_W1 = 169,
|
||||||
|
REG_W2 = 170,
|
||||||
|
REG_W3 = 171,
|
||||||
|
REG_W4 = 172,
|
||||||
|
REG_W5 = 173,
|
||||||
|
REG_W6 = 174,
|
||||||
|
REG_W7 = 175,
|
||||||
|
REG_W8 = 176,
|
||||||
|
REG_W9 = 177,
|
||||||
|
REG_W10 = 178,
|
||||||
|
REG_W11 = 179,
|
||||||
|
REG_W12 = 180,
|
||||||
|
REG_W13 = 181,
|
||||||
|
REG_W14 = 182,
|
||||||
|
REG_W15 = 183,
|
||||||
|
REG_W16 = 184,
|
||||||
|
REG_W17 = 185,
|
||||||
|
REG_W18 = 186,
|
||||||
|
REG_W19 = 187,
|
||||||
|
REG_W20 = 188,
|
||||||
|
REG_W21 = 189,
|
||||||
|
REG_W22 = 190,
|
||||||
|
REG_W23 = 191,
|
||||||
|
REG_W24 = 192,
|
||||||
|
REG_W25 = 193,
|
||||||
|
REG_W26 = 194,
|
||||||
|
REG_W27 = 195,
|
||||||
|
REG_W28 = 196,
|
||||||
|
REG_W29 = 197,
|
||||||
|
REG_W30 = 198,
|
||||||
|
REG_X0 = 199,
|
||||||
|
REG_X1 = 200,
|
||||||
|
REG_X2 = 201,
|
||||||
|
REG_X3 = 202,
|
||||||
|
REG_X4 = 203,
|
||||||
|
REG_X5 = 204,
|
||||||
|
REG_X6 = 205,
|
||||||
|
REG_X7 = 206,
|
||||||
|
REG_X8 = 207,
|
||||||
|
REG_X9 = 208,
|
||||||
|
REG_X10 = 209,
|
||||||
|
REG_X11 = 210,
|
||||||
|
REG_X12 = 211,
|
||||||
|
REG_X13 = 212,
|
||||||
|
REG_X14 = 213,
|
||||||
|
REG_X15 = 214,
|
||||||
|
REG_X16 = 215,
|
||||||
|
REG_X17 = 216,
|
||||||
|
REG_X18 = 217,
|
||||||
|
REG_X19 = 218,
|
||||||
|
REG_X20 = 219,
|
||||||
|
REG_X21 = 220,
|
||||||
|
REG_X22 = 221,
|
||||||
|
REG_X23 = 222,
|
||||||
|
REG_X24 = 223,
|
||||||
|
REG_X25 = 224,
|
||||||
|
REG_X26 = 225,
|
||||||
|
REG_X27 = 226,
|
||||||
|
REG_X28 = 227,
|
||||||
|
REG_V0 = 228,
|
||||||
|
REG_V1 = 229,
|
||||||
|
REG_V2 = 230,
|
||||||
|
REG_V3 = 231,
|
||||||
|
REG_V4 = 232,
|
||||||
|
REG_V5 = 233,
|
||||||
|
REG_V6 = 234,
|
||||||
|
REG_V7 = 235,
|
||||||
|
REG_V8 = 236,
|
||||||
|
REG_V9 = 237,
|
||||||
|
REG_V10 = 238,
|
||||||
|
REG_V11 = 239,
|
||||||
|
REG_V12 = 240,
|
||||||
|
REG_V13 = 241,
|
||||||
|
REG_V14 = 242,
|
||||||
|
REG_V15 = 243,
|
||||||
|
REG_V16 = 244,
|
||||||
|
REG_V17 = 245,
|
||||||
|
REG_V18 = 246,
|
||||||
|
REG_V19 = 247,
|
||||||
|
REG_V20 = 248,
|
||||||
|
REG_V21 = 249,
|
||||||
|
REG_V22 = 250,
|
||||||
|
REG_V23 = 251,
|
||||||
|
REG_V24 = 252,
|
||||||
|
REG_V25 = 253,
|
||||||
|
REG_V26 = 254,
|
||||||
|
REG_V27 = 255,
|
||||||
|
REG_V28 = 256,
|
||||||
|
REG_V29 = 257,
|
||||||
|
REG_V30 = 258,
|
||||||
|
REG_V31 = 259,
|
||||||
|
|
||||||
|
// pseudo registers
|
||||||
|
REG_PC = 260,
|
||||||
|
REG_CPACR_EL1 = 261,
|
||||||
|
|
||||||
|
// thread registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
||||||
|
REG_TPIDR_EL0 = 262,
|
||||||
|
REG_TPIDRRO_EL0 = 263,
|
||||||
|
REG_TPIDR_EL1 = 264,
|
||||||
|
REG_PSTATE = 265,
|
||||||
|
|
||||||
|
// exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
||||||
|
REG_ELR_EL0 = 266,
|
||||||
|
REG_ELR_EL1 = 267,
|
||||||
|
REG_ELR_EL2 = 268,
|
||||||
|
REG_ELR_EL3 = 269,
|
||||||
|
|
||||||
|
// stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
||||||
|
REG_SP_EL0 = 270,
|
||||||
|
REG_SP_EL1 = 271,
|
||||||
|
REG_SP_EL2 = 272,
|
||||||
|
REG_SP_EL3 = 273,
|
||||||
|
|
||||||
|
// other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
||||||
|
REG_TTBR0_EL1 = 274,
|
||||||
|
REG_TTBR1_EL1 = 275,
|
||||||
|
REG_ESR_EL0 = 276,
|
||||||
|
REG_ESR_EL1 = 277,
|
||||||
|
REG_ESR_EL2 = 278,
|
||||||
|
REG_ESR_EL3 = 279,
|
||||||
|
REG_FAR_EL0 = 280,
|
||||||
|
REG_FAR_EL1 = 281,
|
||||||
|
REG_FAR_EL2 = 282,
|
||||||
|
REG_FAR_EL3 = 283,
|
||||||
|
REG_PAR_EL1 = 284,
|
||||||
|
REG_MAIR_EL1 = 285,
|
||||||
|
REG_VBAR_EL0 = 286,
|
||||||
|
REG_VBAR_EL1 = 287,
|
||||||
|
REG_VBAR_EL2 = 288,
|
||||||
|
REG_VBAR_EL3 = 289,
|
||||||
|
REG_CP_REG = 290,
|
||||||
|
|
||||||
|
// floating point control and status registers
|
||||||
|
REG_FPCR = 291,
|
||||||
|
REG_FPSR = 292,
|
||||||
|
REG_ENDING = 293,
|
||||||
|
|
||||||
|
// alias registers
|
||||||
|
REG_IP0 = 215,
|
||||||
|
REG_IP1 = 216,
|
||||||
|
REG_FP = 1,
|
||||||
|
REG_LR = 2,
|
||||||
|
|
||||||
|
// ARM64 instructions
|
||||||
|
|
||||||
|
INS_INVALID = 0,
|
||||||
|
INS_MRS = 1,
|
||||||
|
INS_MSR = 2,
|
||||||
|
INS_SYS = 3,
|
||||||
|
INS_SYSL = 4,
|
||||||
|
INS_ENDING = 5,
|
||||||
|
}
|
||||||
|
}
|
44
Ryujinx.Tests.Unicorn/Native/Const/Common.cs
Normal file
44
Ryujinx.Tests.Unicorn/Native/Const/Common.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Common
|
||||||
|
{
|
||||||
|
API_MAJOR = 2,
|
||||||
|
|
||||||
|
API_MINOR = 0,
|
||||||
|
|
||||||
|
API_PATCH = 0,
|
||||||
|
API_EXTRA = 255,
|
||||||
|
VERSION_MAJOR = 2,
|
||||||
|
|
||||||
|
VERSION_MINOR = 0,
|
||||||
|
|
||||||
|
VERSION_PATCH = 0,
|
||||||
|
VERSION_EXTRA = 255,
|
||||||
|
SECOND_SCALE = 1000000,
|
||||||
|
MILISECOND_SCALE = 1000,
|
||||||
|
QUERY_MODE = 1,
|
||||||
|
QUERY_PAGE_SIZE = 2,
|
||||||
|
QUERY_ARCH = 3,
|
||||||
|
QUERY_TIMEOUT = 4,
|
||||||
|
|
||||||
|
CTL_IO_NONE = 0,
|
||||||
|
CTL_IO_WRITE = 1,
|
||||||
|
CTL_IO_READ = 2,
|
||||||
|
CTL_IO_READ_WRITE = 3,
|
||||||
|
|
||||||
|
CTL_UC_MODE = 0,
|
||||||
|
CTL_UC_PAGE_SIZE = 1,
|
||||||
|
CTL_UC_ARCH = 2,
|
||||||
|
CTL_UC_TIMEOUT = 3,
|
||||||
|
CTL_UC_USE_EXITS = 4,
|
||||||
|
CTL_UC_EXITS_CNT = 5,
|
||||||
|
CTL_UC_EXITS = 6,
|
||||||
|
CTL_CPU_MODEL = 7,
|
||||||
|
CTL_TB_REQUEST_CACHE = 8,
|
||||||
|
CTL_TB_REMOVE_CACHE = 9,
|
||||||
|
CTL_TB_FLUSH = 10,
|
||||||
|
}
|
||||||
|
}
|
31
Ryujinx.Tests.Unicorn/Native/Const/Error.cs
Normal file
31
Ryujinx.Tests.Unicorn/Native/Const/Error.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Error
|
||||||
|
{
|
||||||
|
OK = 0,
|
||||||
|
NOMEM = 1,
|
||||||
|
ARCH = 2,
|
||||||
|
HANDLE = 3,
|
||||||
|
MODE = 4,
|
||||||
|
VERSION = 5,
|
||||||
|
READ_UNMAPPED = 6,
|
||||||
|
WRITE_UNMAPPED = 7,
|
||||||
|
FETCH_UNMAPPED = 8,
|
||||||
|
HOOK = 9,
|
||||||
|
INSN_INVALID = 10,
|
||||||
|
MAP = 11,
|
||||||
|
WRITE_PROT = 12,
|
||||||
|
READ_PROT = 13,
|
||||||
|
FETCH_PROT = 14,
|
||||||
|
ARG = 15,
|
||||||
|
READ_UNALIGNED = 16,
|
||||||
|
WRITE_UNALIGNED = 17,
|
||||||
|
FETCH_UNALIGNED = 18,
|
||||||
|
HOOK_EXIST = 19,
|
||||||
|
RESOURCE = 20,
|
||||||
|
EXCEPTION = 21,
|
||||||
|
}
|
||||||
|
}
|
33
Ryujinx.Tests.Unicorn/Native/Const/Hook.cs
Normal file
33
Ryujinx.Tests.Unicorn/Native/Const/Hook.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Hook
|
||||||
|
{
|
||||||
|
INTR = 1,
|
||||||
|
INSN = 2,
|
||||||
|
CODE = 4,
|
||||||
|
BLOCK = 8,
|
||||||
|
MEM_READ_UNMAPPED = 16,
|
||||||
|
MEM_WRITE_UNMAPPED = 32,
|
||||||
|
MEM_FETCH_UNMAPPED = 64,
|
||||||
|
MEM_READ_PROT = 128,
|
||||||
|
MEM_WRITE_PROT = 256,
|
||||||
|
MEM_FETCH_PROT = 512,
|
||||||
|
MEM_READ = 1024,
|
||||||
|
MEM_WRITE = 2048,
|
||||||
|
MEM_FETCH = 4096,
|
||||||
|
MEM_READ_AFTER = 8192,
|
||||||
|
INSN_INVALID = 16384,
|
||||||
|
EDGE_GENERATED = 32768,
|
||||||
|
TCG_OPCODE = 65536,
|
||||||
|
MEM_UNMAPPED = 112,
|
||||||
|
MEM_PROT = 896,
|
||||||
|
MEM_READ_INVALID = 144,
|
||||||
|
MEM_WRITE_INVALID = 288,
|
||||||
|
MEM_FETCH_INVALID = 576,
|
||||||
|
MEM_INVALID = 1008,
|
||||||
|
MEM_VALID = 7168,
|
||||||
|
}
|
||||||
|
}
|
19
Ryujinx.Tests.Unicorn/Native/Const/Memory.cs
Normal file
19
Ryujinx.Tests.Unicorn/Native/Const/Memory.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Memory
|
||||||
|
{
|
||||||
|
READ = 16,
|
||||||
|
WRITE = 17,
|
||||||
|
FETCH = 18,
|
||||||
|
READ_UNMAPPED = 19,
|
||||||
|
WRITE_UNMAPPED = 20,
|
||||||
|
FETCH_UNMAPPED = 21,
|
||||||
|
WRITE_PROT = 22,
|
||||||
|
READ_PROT = 23,
|
||||||
|
FETCH_PROT = 24,
|
||||||
|
READ_AFTER = 25,
|
||||||
|
}
|
||||||
|
}
|
35
Ryujinx.Tests.Unicorn/Native/Const/Mode.cs
Normal file
35
Ryujinx.Tests.Unicorn/Native/Const/Mode.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Mode
|
||||||
|
{
|
||||||
|
LITTLE_ENDIAN = 0,
|
||||||
|
BIG_ENDIAN = 1073741824,
|
||||||
|
ARM = 0,
|
||||||
|
THUMB = 16,
|
||||||
|
MCLASS = 32,
|
||||||
|
V8 = 64,
|
||||||
|
ARMBE8 = 1024,
|
||||||
|
ARM926 = 128,
|
||||||
|
ARM946 = 256,
|
||||||
|
ARM1176 = 512,
|
||||||
|
MICRO = 16,
|
||||||
|
MIPS3 = 32,
|
||||||
|
MIPS32R6 = 64,
|
||||||
|
MIPS32 = 4,
|
||||||
|
MIPS64 = 8,
|
||||||
|
MODE_16 = 2,
|
||||||
|
MODE_32 = 4,
|
||||||
|
MODE_64 = 8,
|
||||||
|
PPC32 = 4,
|
||||||
|
PPC64 = 8,
|
||||||
|
QPX = 16,
|
||||||
|
SPARC32 = 4,
|
||||||
|
SPARC64 = 8,
|
||||||
|
V9 = 16,
|
||||||
|
RISCV32 = 4,
|
||||||
|
RISCV64 = 8,
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Tests.Unicorn/Native/Const/Permission.cs
Normal file
14
Ryujinx.Tests.Unicorn/Native/Const/Permission.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum Permission
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
READ = 1,
|
||||||
|
WRITE = 2,
|
||||||
|
EXEC = 4,
|
||||||
|
ALL = 7,
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Tests.Unicorn/Native/Const/TCG.cs
Normal file
12
Ryujinx.Tests.Unicorn/Native/Const/TCG.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
namespace Ryujinx.Tests.Unicorn.Native.Const
|
||||||
|
{
|
||||||
|
public enum TCG
|
||||||
|
{
|
||||||
|
OP_SUB = 0,
|
||||||
|
OP_FLAG_CMP = 1,
|
||||||
|
OP_FLAG_DIRECT = 2,
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,43 @@
|
|||||||
|
using Ryujinx.Tests.Unicorn.Native.Const;
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
namespace Ryujinx.Tests.Unicorn.Native
|
||||||
{
|
{
|
||||||
public class Interface
|
public static class Interface
|
||||||
{
|
{
|
||||||
public static void Checked(UnicornError error)
|
public static bool IsUnicornAvailable { get; private set; } = true;
|
||||||
|
|
||||||
|
private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||||
{
|
{
|
||||||
if (error != UnicornError.UC_ERR_OK)
|
if (libraryName == "unicorn")
|
||||||
|
{
|
||||||
|
string loadPath = $"{Path.GetDirectoryName(assembly.Location)}/";
|
||||||
|
loadPath += OperatingSystem.IsWindows() ? $"{libraryName}.dll" : $"lib{libraryName}.so";
|
||||||
|
|
||||||
|
if (!NativeLibrary.TryLoad(loadPath, out IntPtr libraryPtr))
|
||||||
|
{
|
||||||
|
IsUnicornAvailable = false;
|
||||||
|
Console.Error.WriteLine($"ERROR: Could not find unicorn at: {loadPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return libraryPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, fallback to default import resolver.
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Interface()
|
||||||
|
{
|
||||||
|
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), ImportResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Checked(Error error)
|
||||||
|
{
|
||||||
|
if (error != Error.OK)
|
||||||
{
|
{
|
||||||
throw new UnicornException(error);
|
throw new UnicornException(error);
|
||||||
}
|
}
|
||||||
@@ -31,39 +61,39 @@ namespace Ryujinx.Tests.Unicorn.Native
|
|||||||
public static extern uint uc_version(out uint major, out uint minor);
|
public static extern uint uc_version(out uint major, out uint minor);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_open(UnicornArch arch, UnicornMode mode, out IntPtr uc);
|
public static extern Error uc_open(Arch arch, Mode mode, out IntPtr uc);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_close(IntPtr uc);
|
public static extern Error uc_close(IntPtr uc);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern IntPtr uc_strerror(UnicornError err);
|
public static extern IntPtr uc_strerror(Error err);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_reg_write(IntPtr uc, int regid, byte[] value);
|
public static extern Error uc_reg_write(IntPtr uc, int regid, byte[] value);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_reg_read(IntPtr uc, int regid, byte[] value);
|
public static extern Error uc_reg_read(IntPtr uc, int regid, byte[] value);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
public static extern Error uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
public static extern Error uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
|
public static extern Error uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
|
public static extern Error uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_mem_unmap(IntPtr uc, ulong address, ulong size);
|
public static extern Error uc_mem_unmap(IntPtr uc, ulong address, ulong size);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
|
public static extern Error uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
|
||||||
|
|
||||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern UnicornError uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
|
public static extern Error uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,14 +0,0 @@
|
|||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
public enum UnicornArch : uint
|
|
||||||
{
|
|
||||||
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
|
|
||||||
UC_ARCH_ARM64, // ARM-64, also called AArch64
|
|
||||||
UC_ARCH_MIPS, // Mips architecture
|
|
||||||
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
|
|
||||||
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
|
|
||||||
UC_ARCH_SPARC, // Sparc architecture
|
|
||||||
UC_ARCH_M68K, // M68K architecture
|
|
||||||
UC_ARCH_MAX,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,33 +0,0 @@
|
|||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
public enum UnicornMode : uint
|
|
||||||
{
|
|
||||||
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
|
|
||||||
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
|
|
||||||
// arm / arm64
|
|
||||||
UC_MODE_ARM = 0, // ARM mode
|
|
||||||
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
|
|
||||||
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
|
|
||||||
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
|
|
||||||
// mips
|
|
||||||
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
|
|
||||||
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
|
|
||||||
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
|
|
||||||
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
|
|
||||||
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
|
|
||||||
// x86 / x64
|
|
||||||
UC_MODE_16 = 1 << 1, // 16-bit mode
|
|
||||||
UC_MODE_32 = 1 << 2, // 32-bit mode
|
|
||||||
UC_MODE_64 = 1 << 3, // 64-bit mode
|
|
||||||
// ppc
|
|
||||||
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
|
|
||||||
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
|
|
||||||
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
|
|
||||||
// sparc
|
|
||||||
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
|
|
||||||
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
|
|
||||||
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
|
|
||||||
// m68k
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native;
|
using Ryujinx.Tests.Unicorn.Native;
|
||||||
|
using Ryujinx.Tests.Unicorn.Native.Const;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
namespace Ryujinx.Tests.Unicorn
|
||||||
@@ -30,32 +31,32 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
|
|
||||||
public uint LR
|
public uint LR
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm32Register.LR);
|
get => GetRegister(Arm.REG_LR);
|
||||||
set => SetRegister(Arm32Register.LR, value);
|
set => SetRegister(Arm.REG_LR, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint SP
|
public uint SP
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm32Register.SP);
|
get => GetRegister(Arm.REG_SP);
|
||||||
set => SetRegister(Arm32Register.SP, value);
|
set => SetRegister(Arm.REG_SP, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint PC
|
public uint PC
|
||||||
{
|
{
|
||||||
get => GetRegister(Arm32Register.PC) & 0xfffffffeu;
|
get => GetRegister(Arm.REG_PC) & 0xfffffffeu;
|
||||||
set => SetRegister(Arm32Register.PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u));
|
set => SetRegister(Arm.REG_PC, (value & 0xfffffffeu) | (ThumbFlag ? 1u : 0u));
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint CPSR
|
public uint CPSR
|
||||||
{
|
{
|
||||||
get => (uint)GetRegister(Arm32Register.CPSR);
|
get => GetRegister(Arm.REG_CPSR);
|
||||||
set => SetRegister(Arm32Register.CPSR, (uint)value);
|
set => SetRegister(Arm.REG_CPSR, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpscr
|
public int Fpscr
|
||||||
{
|
{
|
||||||
get => (int)GetRegister(Arm32Register.FPSCR) | ((int)GetRegister(Arm32Register.FPSCR_NZCV));
|
get => (int)GetRegister(Arm.REG_FPSCR) | ((int)GetRegister(Arm.REG_FPSCR_NZCV));
|
||||||
set => SetRegister(Arm32Register.FPSCR, (uint)value);
|
set => SetRegister(Arm.REG_FPSCR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool QFlag
|
public bool QFlag
|
||||||
@@ -94,16 +95,16 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u);
|
CPSR = (CPSR & ~0x00000020u) | (value ? 0x00000020u : 0u);
|
||||||
SetRegister(Arm32Register.PC, (GetRegister(Arm32Register.PC) & 0xfffffffeu) | (value ? 1u : 0u));
|
SetRegister(Arm.REG_PC, (GetRegister(Arm.REG_PC) & 0xfffffffeu) | (value ? 1u : 0u));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnicornAArch32()
|
public UnicornAArch32()
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
|
Interface.Checked(Interface.uc_open(Arch.ARM, Mode.LITTLE_ENDIAN, out uc));
|
||||||
|
|
||||||
SetRegister(Arm32Register.C1_C0_2, GetRegister(Arm32Register.C1_C0_2) | 0xf00000);
|
SetRegister(Arm.REG_C1_C0_2, GetRegister(Arm.REG_C1_C0_2) | 0xf00000);
|
||||||
SetRegister(Arm32Register.FPEXC, 0x40000000);
|
SetRegister(Arm.REG_FPEXC, 0x40000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
~UnicornAArch32()
|
~UnicornAArch32()
|
||||||
@@ -136,44 +137,44 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
RunForCount(1);
|
RunForCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Arm32Register[] XRegisters = new Arm32Register[16]
|
private static Arm[] XRegisters = new Arm[16]
|
||||||
{
|
{
|
||||||
Arm32Register.R0,
|
Arm.REG_R0,
|
||||||
Arm32Register.R1,
|
Arm.REG_R1,
|
||||||
Arm32Register.R2,
|
Arm.REG_R2,
|
||||||
Arm32Register.R3,
|
Arm.REG_R3,
|
||||||
Arm32Register.R4,
|
Arm.REG_R4,
|
||||||
Arm32Register.R5,
|
Arm.REG_R5,
|
||||||
Arm32Register.R6,
|
Arm.REG_R6,
|
||||||
Arm32Register.R7,
|
Arm.REG_R7,
|
||||||
Arm32Register.R8,
|
Arm.REG_R8,
|
||||||
Arm32Register.R9,
|
Arm.REG_R9,
|
||||||
Arm32Register.R10,
|
Arm.REG_R10,
|
||||||
Arm32Register.R11,
|
Arm.REG_R11,
|
||||||
Arm32Register.R12,
|
Arm.REG_R12,
|
||||||
Arm32Register.R13,
|
Arm.REG_R13,
|
||||||
Arm32Register.R14,
|
Arm.REG_R14,
|
||||||
Arm32Register.R15,
|
Arm.REG_R15,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Arm32Register[] QRegisters = new Arm32Register[16]
|
private static Arm[] QRegisters = new Arm[16]
|
||||||
{
|
{
|
||||||
Arm32Register.Q0,
|
Arm.REG_Q0,
|
||||||
Arm32Register.Q1,
|
Arm.REG_Q1,
|
||||||
Arm32Register.Q2,
|
Arm.REG_Q2,
|
||||||
Arm32Register.Q3,
|
Arm.REG_Q3,
|
||||||
Arm32Register.Q4,
|
Arm.REG_Q4,
|
||||||
Arm32Register.Q5,
|
Arm.REG_Q5,
|
||||||
Arm32Register.Q6,
|
Arm.REG_Q6,
|
||||||
Arm32Register.Q7,
|
Arm.REG_Q7,
|
||||||
Arm32Register.Q8,
|
Arm.REG_Q8,
|
||||||
Arm32Register.Q9,
|
Arm.REG_Q9,
|
||||||
Arm32Register.Q10,
|
Arm.REG_Q10,
|
||||||
Arm32Register.Q11,
|
Arm.REG_Q11,
|
||||||
Arm32Register.Q12,
|
Arm.REG_Q12,
|
||||||
Arm32Register.Q13,
|
Arm.REG_Q13,
|
||||||
Arm32Register.Q14,
|
Arm.REG_Q14,
|
||||||
Arm32Register.Q15
|
Arm.REG_Q15
|
||||||
};
|
};
|
||||||
|
|
||||||
public uint GetX(int index)
|
public uint GetX(int index)
|
||||||
@@ -204,7 +205,7 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead.
|
// Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead.
|
||||||
return GetVector((Arm32Register)((int)Arm32Register.D0 + index * 2));
|
return GetVector((Arm)((int)Arm.REG_D0 + index * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetQ(int index, SimdValue value)
|
public void SetQ(int index, SimdValue value)
|
||||||
@@ -214,10 +215,10 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetVector((Arm32Register)((int)Arm32Register.D0 + index * 2), value);
|
SetVector((Arm)((int)Arm.REG_D0 + index * 2), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetRegister(Arm32Register register)
|
public uint GetRegister(Arm register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[4];
|
byte[] data = new byte[4];
|
||||||
|
|
||||||
@@ -226,14 +227,14 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
return (uint)BitConverter.ToInt32(data, 0);
|
return (uint)BitConverter.ToInt32(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRegister(Arm32Register register, uint value)
|
public void SetRegister(Arm register, uint value)
|
||||||
{
|
{
|
||||||
byte[] data = BitConverter.GetBytes(value);
|
byte[] data = BitConverter.GetBytes(value);
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimdValue GetVector(Arm32Register register)
|
public SimdValue GetVector(Arm register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[8];
|
byte[] data = new byte[8];
|
||||||
|
|
||||||
@@ -245,7 +246,7 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
return new SimdValue(lo, hi);
|
return new SimdValue(lo, hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVector(Arm32Register register, SimdValue value)
|
private void SetVector(Arm register, SimdValue value)
|
||||||
{
|
{
|
||||||
byte[] data = BitConverter.GetBytes(value.GetUInt64(0));
|
byte[] data = BitConverter.GetBytes(value.GetUInt64(0));
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
||||||
@@ -300,13 +301,10 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Interface.uc_version(out _, out _);
|
Interface.uc_version(out _, out _);
|
||||||
|
}
|
||||||
|
catch (DllNotFoundException) { }
|
||||||
|
|
||||||
return true;
|
return Interface.IsUnicornAvailable;
|
||||||
}
|
|
||||||
catch (DllNotFoundException)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native;
|
using Ryujinx.Tests.Unicorn.Native;
|
||||||
|
using Ryujinx.Tests.Unicorn.Native.Const;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
namespace Ryujinx.Tests.Unicorn
|
||||||
@@ -30,38 +31,38 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
|
|
||||||
public ulong LR
|
public ulong LR
|
||||||
{
|
{
|
||||||
get => GetRegister(ArmRegister.LR);
|
get => GetRegister(Arm64.REG_LR);
|
||||||
set => SetRegister(ArmRegister.LR, value);
|
set => SetRegister(Arm64.REG_LR, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong SP
|
public ulong SP
|
||||||
{
|
{
|
||||||
get => GetRegister(ArmRegister.SP);
|
get => GetRegister(Arm64.REG_SP);
|
||||||
set => SetRegister(ArmRegister.SP, value);
|
set => SetRegister(Arm64.REG_SP, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong PC
|
public ulong PC
|
||||||
{
|
{
|
||||||
get => GetRegister(ArmRegister.PC);
|
get => GetRegister(Arm64.REG_PC);
|
||||||
set => SetRegister(ArmRegister.PC, value);
|
set => SetRegister(Arm64.REG_PC, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Pstate
|
public uint Pstate
|
||||||
{
|
{
|
||||||
get => (uint)GetRegister(ArmRegister.PSTATE);
|
get => (uint)GetRegister(Arm64.REG_PSTATE);
|
||||||
set => SetRegister(ArmRegister.PSTATE, (uint)value);
|
set => SetRegister(Arm64.REG_PSTATE, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpcr
|
public int Fpcr
|
||||||
{
|
{
|
||||||
get => (int)GetRegister(ArmRegister.FPCR);
|
get => (int)GetRegister(Arm64.REG_FPCR);
|
||||||
set => SetRegister(ArmRegister.FPCR, (uint)value);
|
set => SetRegister(Arm64.REG_FPCR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Fpsr
|
public int Fpsr
|
||||||
{
|
{
|
||||||
get => (int)GetRegister(ArmRegister.FPSR);
|
get => (int)GetRegister(Arm64.REG_FPSR);
|
||||||
set => SetRegister(ArmRegister.FPSR, (uint)value);
|
set => SetRegister(Arm64.REG_FPSR, (uint)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OverflowFlag
|
public bool OverflowFlag
|
||||||
@@ -90,9 +91,9 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
|
|
||||||
public UnicornAArch64()
|
public UnicornAArch64()
|
||||||
{
|
{
|
||||||
Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM64, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
|
Interface.Checked(Interface.uc_open(Arch.ARM64, Mode.LITTLE_ENDIAN, out uc));
|
||||||
|
|
||||||
SetRegister(ArmRegister.CPACR_EL1, 0x00300000);
|
SetRegister(Arm64.REG_CPACR_EL1, 0x00300000);
|
||||||
}
|
}
|
||||||
|
|
||||||
~UnicornAArch64()
|
~UnicornAArch64()
|
||||||
@@ -125,75 +126,75 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
RunForCount(1);
|
RunForCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArmRegister[] XRegisters = new ArmRegister[31]
|
private static Arm64[] XRegisters = new Arm64[31]
|
||||||
{
|
{
|
||||||
ArmRegister.X0,
|
Arm64.REG_X0,
|
||||||
ArmRegister.X1,
|
Arm64.REG_X1,
|
||||||
ArmRegister.X2,
|
Arm64.REG_X2,
|
||||||
ArmRegister.X3,
|
Arm64.REG_X3,
|
||||||
ArmRegister.X4,
|
Arm64.REG_X4,
|
||||||
ArmRegister.X5,
|
Arm64.REG_X5,
|
||||||
ArmRegister.X6,
|
Arm64.REG_X6,
|
||||||
ArmRegister.X7,
|
Arm64.REG_X7,
|
||||||
ArmRegister.X8,
|
Arm64.REG_X8,
|
||||||
ArmRegister.X9,
|
Arm64.REG_X9,
|
||||||
ArmRegister.X10,
|
Arm64.REG_X10,
|
||||||
ArmRegister.X11,
|
Arm64.REG_X11,
|
||||||
ArmRegister.X12,
|
Arm64.REG_X12,
|
||||||
ArmRegister.X13,
|
Arm64.REG_X13,
|
||||||
ArmRegister.X14,
|
Arm64.REG_X14,
|
||||||
ArmRegister.X15,
|
Arm64.REG_X15,
|
||||||
ArmRegister.X16,
|
Arm64.REG_X16,
|
||||||
ArmRegister.X17,
|
Arm64.REG_X17,
|
||||||
ArmRegister.X18,
|
Arm64.REG_X18,
|
||||||
ArmRegister.X19,
|
Arm64.REG_X19,
|
||||||
ArmRegister.X20,
|
Arm64.REG_X20,
|
||||||
ArmRegister.X21,
|
Arm64.REG_X21,
|
||||||
ArmRegister.X22,
|
Arm64.REG_X22,
|
||||||
ArmRegister.X23,
|
Arm64.REG_X23,
|
||||||
ArmRegister.X24,
|
Arm64.REG_X24,
|
||||||
ArmRegister.X25,
|
Arm64.REG_X25,
|
||||||
ArmRegister.X26,
|
Arm64.REG_X26,
|
||||||
ArmRegister.X27,
|
Arm64.REG_X27,
|
||||||
ArmRegister.X28,
|
Arm64.REG_X28,
|
||||||
ArmRegister.X29,
|
Arm64.REG_X29,
|
||||||
ArmRegister.X30,
|
Arm64.REG_X30,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static ArmRegister[] QRegisters = new ArmRegister[32]
|
private static Arm64[] QRegisters = new Arm64[32]
|
||||||
{
|
{
|
||||||
ArmRegister.Q0,
|
Arm64.REG_Q0,
|
||||||
ArmRegister.Q1,
|
Arm64.REG_Q1,
|
||||||
ArmRegister.Q2,
|
Arm64.REG_Q2,
|
||||||
ArmRegister.Q3,
|
Arm64.REG_Q3,
|
||||||
ArmRegister.Q4,
|
Arm64.REG_Q4,
|
||||||
ArmRegister.Q5,
|
Arm64.REG_Q5,
|
||||||
ArmRegister.Q6,
|
Arm64.REG_Q6,
|
||||||
ArmRegister.Q7,
|
Arm64.REG_Q7,
|
||||||
ArmRegister.Q8,
|
Arm64.REG_Q8,
|
||||||
ArmRegister.Q9,
|
Arm64.REG_Q9,
|
||||||
ArmRegister.Q10,
|
Arm64.REG_Q10,
|
||||||
ArmRegister.Q11,
|
Arm64.REG_Q11,
|
||||||
ArmRegister.Q12,
|
Arm64.REG_Q12,
|
||||||
ArmRegister.Q13,
|
Arm64.REG_Q13,
|
||||||
ArmRegister.Q14,
|
Arm64.REG_Q14,
|
||||||
ArmRegister.Q15,
|
Arm64.REG_Q15,
|
||||||
ArmRegister.Q16,
|
Arm64.REG_Q16,
|
||||||
ArmRegister.Q17,
|
Arm64.REG_Q17,
|
||||||
ArmRegister.Q18,
|
Arm64.REG_Q18,
|
||||||
ArmRegister.Q19,
|
Arm64.REG_Q19,
|
||||||
ArmRegister.Q20,
|
Arm64.REG_Q20,
|
||||||
ArmRegister.Q21,
|
Arm64.REG_Q21,
|
||||||
ArmRegister.Q22,
|
Arm64.REG_Q22,
|
||||||
ArmRegister.Q23,
|
Arm64.REG_Q23,
|
||||||
ArmRegister.Q24,
|
Arm64.REG_Q24,
|
||||||
ArmRegister.Q25,
|
Arm64.REG_Q25,
|
||||||
ArmRegister.Q26,
|
Arm64.REG_Q26,
|
||||||
ArmRegister.Q27,
|
Arm64.REG_Q27,
|
||||||
ArmRegister.Q28,
|
Arm64.REG_Q28,
|
||||||
ArmRegister.Q29,
|
Arm64.REG_Q29,
|
||||||
ArmRegister.Q30,
|
Arm64.REG_Q30,
|
||||||
ArmRegister.Q31,
|
Arm64.REG_Q31,
|
||||||
};
|
};
|
||||||
|
|
||||||
public ulong GetX(int index)
|
public ulong GetX(int index)
|
||||||
@@ -236,7 +237,7 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
SetVector(QRegisters[index], value);
|
SetVector(QRegisters[index], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong GetRegister(ArmRegister register)
|
private ulong GetRegister(Arm64 register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[8];
|
byte[] data = new byte[8];
|
||||||
|
|
||||||
@@ -245,14 +246,14 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
return (ulong)BitConverter.ToInt64(data, 0);
|
return (ulong)BitConverter.ToInt64(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetRegister(ArmRegister register, ulong value)
|
private void SetRegister(Arm64 register, ulong value)
|
||||||
{
|
{
|
||||||
byte[] data = BitConverter.GetBytes(value);
|
byte[] data = BitConverter.GetBytes(value);
|
||||||
|
|
||||||
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimdValue GetVector(ArmRegister register)
|
private SimdValue GetVector(Arm64 register)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[16];
|
byte[] data = new byte[16];
|
||||||
|
|
||||||
@@ -261,7 +262,7 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
return new SimdValue(data);
|
return new SimdValue(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVector(ArmRegister register, SimdValue value)
|
private void SetVector(Arm64 register, SimdValue value)
|
||||||
{
|
{
|
||||||
byte[] data = value.ToArray();
|
byte[] data = value.ToArray();
|
||||||
|
|
||||||
@@ -315,13 +316,10 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Interface.uc_version(out _, out _);
|
Interface.uc_version(out _, out _);
|
||||||
|
}
|
||||||
|
catch (DllNotFoundException) { }
|
||||||
|
|
||||||
return true;
|
return Interface.IsUnicornAvailable;
|
||||||
}
|
|
||||||
catch (DllNotFoundException)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,29 +0,0 @@
|
|||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn
|
|
||||||
{
|
|
||||||
public enum UnicornError
|
|
||||||
{
|
|
||||||
UC_ERR_OK = 0, // No error: everything was fine
|
|
||||||
UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate()
|
|
||||||
UC_ERR_ARCH, // Unsupported architecture: uc_open()
|
|
||||||
UC_ERR_HANDLE, // Invalid handle
|
|
||||||
UC_ERR_MODE, // Invalid/unsupported mode: uc_open()
|
|
||||||
UC_ERR_VERSION, // Unsupported version (bindings)
|
|
||||||
UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
|
|
||||||
UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
|
|
||||||
UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
|
|
||||||
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
|
|
||||||
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
|
|
||||||
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
|
|
||||||
UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
|
|
||||||
UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
|
|
||||||
UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
|
|
||||||
UC_ERR_ARG, // Invalid argument provided to uc_xxx function (See specific function API)
|
|
||||||
UC_ERR_READ_UNALIGNED, // Unaligned read
|
|
||||||
UC_ERR_WRITE_UNALIGNED, // Unaligned write
|
|
||||||
UC_ERR_FETCH_UNALIGNED, // Unaligned fetch
|
|
||||||
UC_ERR_HOOK_EXIST, // hook for this event already existed
|
|
||||||
UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start()
|
|
||||||
UC_ERR_EXCEPTION // Unhandled CPU exception
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Tests.Unicorn.Native.Const;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -5,9 +6,9 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
{
|
{
|
||||||
public class UnicornException : Exception
|
public class UnicornException : Exception
|
||||||
{
|
{
|
||||||
public readonly UnicornError Error;
|
public readonly Error Error;
|
||||||
|
|
||||||
internal UnicornException(UnicornError error)
|
internal UnicornException(Error error)
|
||||||
{
|
{
|
||||||
Error = error;
|
Error = error;
|
||||||
}
|
}
|
||||||
@@ -20,4 +21,4 @@ namespace Ryujinx.Tests.Unicorn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,14 +9,12 @@ CPU simulator, Armeilleure.
|
|||||||
|
|
||||||
On Windows, Unicorn is shipped as a pre-compiled dynamic library (`.dll`), licenced under the GPLv2.
|
On Windows, Unicorn is shipped as a pre-compiled dynamic library (`.dll`), licenced under the GPLv2.
|
||||||
|
|
||||||
The source code for `windows/unicorn.dll` is available at: https://github.com/MerryMage/UnicornDotNet/tree/299451c02d9c810d2feca51f5e9cb6d8b2f38960
|
The source code for `windows/unicorn.dll` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
On Linux, you will first need to download Unicorn from https://github.com/unicorn-engine/unicorn.
|
On Windows, Unicorn is shipped as a pre-compiled shared object (`.so`), licenced under the GPLv2.
|
||||||
|
|
||||||
Then you need to patch it to expose the FSPCR register by applying `linux/unicorn_fspcr.patch`
|
The source code for `linux/unicorn.so` is available at: https://github.com/unicorn-engine/unicorn/tree/df3aa0fccbce9e1420e82110cbae5951755a0698
|
||||||
|
|
||||||
Then, compile Unicorn from source with its `make.sh` script.
|
|
||||||
|
|
||||||
See https://github.com/Ryujinx/Ryujinx/pull/1433 for details.
|
See https://github.com/Ryujinx/Ryujinx/pull/1433 for details.
|
||||||
|
BIN
Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so
Normal file
BIN
Ryujinx.Tests.Unicorn/libs/linux/libunicorn.so
Normal file
Binary file not shown.
@@ -1,24 +0,0 @@
|
|||||||
diff --git a/qemu/target-arm/unicorn_arm.c b/qemu/target-arm/unicorn_arm.c
|
|
||||||
index 5ff9ebb..d4953f4 100644
|
|
||||||
--- a/qemu/target-arm/unicorn_arm.c
|
|
||||||
+++ b/qemu/target-arm/unicorn_arm.c
|
|
||||||
@@ -101,6 +101,9 @@ int arm_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int coun
|
|
||||||
case UC_ARM_REG_FPEXC:
|
|
||||||
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.vfp.xregs[ARM_VFP_FPEXC];
|
|
||||||
break;
|
|
||||||
+ case UC_ARM_REG_FPSCR:
|
|
||||||
+ *(int32_t *)value = vfp_get_fpscr(&ARM_CPU(uc, mycpu)->env);
|
|
||||||
+ break;
|
|
||||||
case UC_ARM_REG_IPSR:
|
|
||||||
*(uint32_t *)value = xpsr_read(&ARM_CPU(uc, mycpu)->env) & 0x1ff;
|
|
||||||
break;
|
|
||||||
@@ -175,6 +178,9 @@ int arm_reg_write(struct uc_struct *uc, unsigned int *regs, void* const* vals, i
|
|
||||||
case UC_ARM_REG_FPEXC:
|
|
||||||
ARM_CPU(uc, mycpu)->env.vfp.xregs[ARM_VFP_FPEXC] = *(int32_t *)value;
|
|
||||||
break;
|
|
||||||
+ case UC_ARM_REG_FPSCR:
|
|
||||||
+ vfp_set_fpscr(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value);
|
|
||||||
+ break;
|
|
||||||
case UC_ARM_REG_IPSR:
|
|
||||||
xpsr_write(&ARM_CPU(uc, mycpu)->env, *(uint32_t *)value, 0x1ff);
|
|
||||||
break;
|
|
Binary file not shown.
199
Ryujinx.Tests.Unicorn/unicorn_const_generator.py
Normal file
199
Ryujinx.Tests.Unicorn/unicorn_const_generator.py
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Unicorn Engine
|
||||||
|
# By Dang Hoang Vu, 2013
|
||||||
|
# Modified for Ryujinx from: https://github.com/unicorn-engine/unicorn/blob/6c1cbef6ac505d355033aef1176b684d02e1eb3a/bindings/const_generator.py
|
||||||
|
from __future__ import print_function
|
||||||
|
import sys, re, os
|
||||||
|
|
||||||
|
include = [ 'arm.h', 'arm64.h', 'unicorn.h' ]
|
||||||
|
split_common = [ 'ARCH', 'MODE', 'ERR', 'MEM', 'TCG', 'HOOK', 'PROT' ]
|
||||||
|
|
||||||
|
template = {
|
||||||
|
'dotnet': {
|
||||||
|
'header': "// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT\n\n// ReSharper disable InconsistentNaming\nnamespace Ryujinx.Tests.Unicorn.Native.Const\n{\n public enum %s\n {\n",
|
||||||
|
'footer': " }\n}\n",
|
||||||
|
'line_format': ' %s = %s,\n',
|
||||||
|
'out_file': os.path.join(os.path.dirname(__file__), 'Native', 'Const', '%s.cs'),
|
||||||
|
# prefixes for constant filenames of all archs - case sensitive
|
||||||
|
'arm.h': 'Arm',
|
||||||
|
'arm64.h': 'Arm64',
|
||||||
|
'unicorn.h': 'Common',
|
||||||
|
# prefixes for filenames of split_common values - case sensitive
|
||||||
|
'ARCH': 'Arch',
|
||||||
|
'MODE': 'Mode',
|
||||||
|
'ERR': 'Error',
|
||||||
|
'MEM': 'Memory',
|
||||||
|
'TCG': 'TCG',
|
||||||
|
'HOOK': 'Hook',
|
||||||
|
'PROT': 'Permission',
|
||||||
|
'comment_open': ' //',
|
||||||
|
'comment_close': '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# markup for comments to be added to autogen files
|
||||||
|
MARKUP = '//>'
|
||||||
|
|
||||||
|
def gen(unicorn_repo_path):
|
||||||
|
global include
|
||||||
|
include_dir = os.path.join(unicorn_repo_path, 'include', 'unicorn')
|
||||||
|
templ = template["dotnet"]
|
||||||
|
for target in include:
|
||||||
|
prefix = templ[target]
|
||||||
|
outfile = open(templ['out_file'] %(prefix), 'wb') # open as binary prevents windows newlines
|
||||||
|
outfile.write((templ['header'] % (prefix)).encode("utf-8"))
|
||||||
|
if target == 'unicorn.h':
|
||||||
|
prefix = ''
|
||||||
|
for cat in split_common:
|
||||||
|
with open(templ['out_file'] %(templ[cat]), 'wb') as file:
|
||||||
|
file.write((templ['header'] %(templ[cat])).encode("utf-8"))
|
||||||
|
with open(os.path.join(include_dir, target)) as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
previous = {}
|
||||||
|
count = 0
|
||||||
|
skip = 0
|
||||||
|
in_comment = False
|
||||||
|
|
||||||
|
for lno, line in enumerate(lines):
|
||||||
|
if "/*" in line:
|
||||||
|
in_comment = True
|
||||||
|
if "*/" in line:
|
||||||
|
in_comment = False
|
||||||
|
if in_comment:
|
||||||
|
continue
|
||||||
|
if skip > 0:
|
||||||
|
# Due to clang-format, values may come up in the next line
|
||||||
|
skip -= 1
|
||||||
|
continue
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
if line.startswith(MARKUP): # markup for comments
|
||||||
|
outfile.write(("\n%s%s%s\n" %(templ['comment_open'], \
|
||||||
|
line.replace(MARKUP, ''), templ['comment_close'])).encode("utf-8"))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line == '' or line.startswith('//'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
tmp = line.strip().split(',')
|
||||||
|
if len(tmp) >= 2 and tmp[0] != "#define" and not tmp[0].startswith("UC_"):
|
||||||
|
continue
|
||||||
|
for t in tmp:
|
||||||
|
t = t.strip()
|
||||||
|
if not t or t.startswith('//'): continue
|
||||||
|
f = re.split('\s+', t)
|
||||||
|
|
||||||
|
# parse #define UC_TARGET (num)
|
||||||
|
define = False
|
||||||
|
if f[0] == '#define' and len(f) >= 3:
|
||||||
|
define = True
|
||||||
|
f.pop(0)
|
||||||
|
f.insert(1, '=')
|
||||||
|
if f[0].startswith("UC_" + prefix.upper()) or f[0].startswith("UC_CPU"):
|
||||||
|
if len(f) > 1 and f[1] not in ('//', '='):
|
||||||
|
print("WARNING: Unable to convert %s" % f)
|
||||||
|
print(" Line =", line)
|
||||||
|
continue
|
||||||
|
elif len(f) > 1 and f[1] == '=':
|
||||||
|
# Like:
|
||||||
|
# UC_A =
|
||||||
|
# (1 << 2)
|
||||||
|
# #define UC_B \
|
||||||
|
# (UC_A | UC_C)
|
||||||
|
# Let's search the next line
|
||||||
|
if len(f) == 2:
|
||||||
|
if lno == len(lines) - 1:
|
||||||
|
print("WARNING: Unable to convert %s" % f)
|
||||||
|
print(" Line =", line)
|
||||||
|
continue
|
||||||
|
skip += 1
|
||||||
|
next_line = lines[lno + 1]
|
||||||
|
next_line_tmp = next_line.strip().split(",")
|
||||||
|
rhs = next_line_tmp[0]
|
||||||
|
elif f[-1] == "\\":
|
||||||
|
idx = 0
|
||||||
|
rhs = ""
|
||||||
|
while True:
|
||||||
|
idx += 1
|
||||||
|
if lno + idx == len(lines):
|
||||||
|
print("WARNING: Unable to convert %s" % f)
|
||||||
|
print(" Line =", line)
|
||||||
|
continue
|
||||||
|
skip += 1
|
||||||
|
next_line = lines[lno + idx]
|
||||||
|
next_line_f = re.split('\s+', next_line.strip())
|
||||||
|
if next_line_f[-1] == "\\":
|
||||||
|
rhs += "".join(next_line_f[:-1])
|
||||||
|
else:
|
||||||
|
rhs += next_line.strip()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
rhs = ''.join(f[2:])
|
||||||
|
else:
|
||||||
|
rhs = str(count)
|
||||||
|
|
||||||
|
|
||||||
|
lhs = f[0].strip()
|
||||||
|
#print(f'lhs: {lhs} rhs: {rhs} f:{f}')
|
||||||
|
# evaluate bitshifts in constants e.g. "UC_X86 = 1 << 1"
|
||||||
|
match = re.match(r'(?P<rhs>\s*\d+\s*<<\s*\d+\s*)', rhs)
|
||||||
|
if match:
|
||||||
|
rhs = str(eval(match.group(1)))
|
||||||
|
else:
|
||||||
|
# evaluate references to other constants e.g. "UC_ARM_REG_X = UC_ARM_REG_SP"
|
||||||
|
match = re.match(r'^([^\d]\w+)$', rhs)
|
||||||
|
if match:
|
||||||
|
rhs = previous[match.group(1)]
|
||||||
|
|
||||||
|
if not rhs.isdigit():
|
||||||
|
for k, v in previous.items():
|
||||||
|
rhs = re.sub(r'\b%s\b' % k, v, rhs)
|
||||||
|
rhs = str(eval(rhs))
|
||||||
|
|
||||||
|
lhs_strip = re.sub(r'^UC_', '', lhs)
|
||||||
|
count = int(rhs) + 1
|
||||||
|
|
||||||
|
if target == "unicorn.h":
|
||||||
|
matched_cat = False
|
||||||
|
for cat in split_common:
|
||||||
|
if lhs_strip.startswith(f"{cat}_"):
|
||||||
|
with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file:
|
||||||
|
cat_lhs_strip = lhs_strip
|
||||||
|
if not lhs_strip.lstrip(f"{cat}_").isnumeric():
|
||||||
|
cat_lhs_strip = lhs_strip.replace(f"{cat}_", "", 1)
|
||||||
|
cat_file.write(
|
||||||
|
(templ['line_format'] % (cat_lhs_strip, rhs)).encode("utf-8"))
|
||||||
|
matched_cat = True
|
||||||
|
break
|
||||||
|
if matched_cat:
|
||||||
|
previous[lhs] = str(rhs)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (count == 1):
|
||||||
|
outfile.write(("\n").encode("utf-8"))
|
||||||
|
|
||||||
|
if lhs_strip.startswith(f"{prefix.upper()}_") and not lhs_strip.replace(f"{prefix.upper()}_", "", 1).isnumeric():
|
||||||
|
lhs_strip = lhs_strip.replace(f"{prefix.upper()}_", "", 1)
|
||||||
|
|
||||||
|
outfile.write((templ['line_format'] % (lhs_strip, rhs)).encode("utf-8"))
|
||||||
|
previous[lhs] = str(rhs)
|
||||||
|
|
||||||
|
outfile.write((templ['footer']).encode("utf-8"))
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
if target == "unicorn.h":
|
||||||
|
for cat in split_common:
|
||||||
|
with open(templ['out_file'] %(templ[cat]), 'ab') as cat_file:
|
||||||
|
cat_file.write(templ['footer'].encode('utf-8'))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage:", sys.argv[0], " <path to unicorn repo>")
|
||||||
|
sys.exit(1)
|
||||||
|
unicorn_repo_path = sys.argv[1]
|
||||||
|
if os.path.isdir(unicorn_repo_path):
|
||||||
|
print("Generating constants for dotnet")
|
||||||
|
gen(unicorn_repo_path)
|
||||||
|
else:
|
||||||
|
print("Couldn't find unicorn repo at:", unicorn_repo_path)
|
@@ -38,14 +38,11 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
private bool _usingMemory;
|
private bool _usingMemory;
|
||||||
|
|
||||||
static CpuTest()
|
[OneTimeSetUp]
|
||||||
|
public void OneTimeSetup()
|
||||||
{
|
{
|
||||||
_unicornAvailable = UnicornAArch64.IsAvailable();
|
_unicornAvailable = UnicornAArch64.IsAvailable();
|
||||||
|
Assume.That(_unicornAvailable, "Unicorn is not available");
|
||||||
if (!_unicornAvailable)
|
|
||||||
{
|
|
||||||
Console.WriteLine("WARNING: Could not find Unicorn.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@@ -610,4 +607,4 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
return rnd & 0x800FFFFFFFFFFFFFul;
|
return rnd & 0x800FFFFFFFFFFFFFul;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -33,14 +33,11 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
private bool _usingMemory;
|
private bool _usingMemory;
|
||||||
|
|
||||||
static CpuTest32()
|
[OneTimeSetUp]
|
||||||
|
public void OneTimeSetup()
|
||||||
{
|
{
|
||||||
_unicornAvailable = UnicornAArch32.IsAvailable();
|
_unicornAvailable = UnicornAArch32.IsAvailable();
|
||||||
|
Assume.That(_unicornAvailable, "Unicorn is not available");
|
||||||
if (!_unicornAvailable)
|
|
||||||
{
|
|
||||||
Console.WriteLine("WARNING: Could not find Unicorn.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
@@ -34,7 +34,17 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="CopyUnicorn" AfterTargets="Build">
|
<Target Name="CopyUnicorn" AfterTargets="Build">
|
||||||
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\unicorn.dll" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
|
<ItemGroup>
|
||||||
|
<UnicornLib Include="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\*unicorn.*"/>
|
||||||
|
</ItemGroup>
|
||||||
|
<Copy SourceFiles="@(UnicornLib)" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="CleanUnicorn" BeforeTargets="Clean">
|
||||||
|
<ItemGroup>
|
||||||
|
<UnicornLib Include="$(OutputPath)/unicorn.*"/>
|
||||||
|
</ItemGroup>
|
||||||
|
<Delete Files="@(UnicornLib)" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -29,7 +29,11 @@ namespace Ryujinx
|
|||||||
|
|
||||||
public static string ConfigurationPath { get; set; }
|
public static string ConfigurationPath { get; set; }
|
||||||
|
|
||||||
[DllImport("libX11")]
|
public static string CommandLineProfile { get; set; }
|
||||||
|
|
||||||
|
private const string X11LibraryName = "libX11";
|
||||||
|
|
||||||
|
[DllImport(X11LibraryName)]
|
||||||
private extern static int XInitThreads();
|
private extern static int XInitThreads();
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
@@ -37,6 +41,30 @@ namespace Ryujinx
|
|||||||
|
|
||||||
private const uint MB_ICONWARNING = 0x30;
|
private const uint MB_ICONWARNING = 0x30;
|
||||||
|
|
||||||
|
static Program()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux())
|
||||||
|
{
|
||||||
|
NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (name, assembly, path) =>
|
||||||
|
{
|
||||||
|
if (name != X11LibraryName)
|
||||||
|
{
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NativeLibrary.TryLoad("libX11.so.6", assembly, path, out IntPtr result))
|
||||||
|
{
|
||||||
|
if (!NativeLibrary.TryLoad("libX11.so", assembly, path, out result))
|
||||||
|
{
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Version = ReleaseInformations.GetVersion();
|
Version = ReleaseInformations.GetVersion();
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||||
<PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
|
<PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
|
||||||
<PackageReference Include="SPB" Version="0.0.4-build27" />
|
<PackageReference Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -148,10 +148,10 @@ namespace Ryujinx.Ui
|
|||||||
|
|
||||||
// Apply custom theme if needed.
|
// Apply custom theme if needed.
|
||||||
ThemeHelper.ApplyTheme();
|
ThemeHelper.ApplyTheme();
|
||||||
|
Gdk.Monitor monitor = Display.GetMonitor(0);
|
||||||
// Sets overridden fields.
|
// Sets overridden fields.
|
||||||
int monitorWidth = Display.PrimaryMonitor.Geometry.Width * Display.PrimaryMonitor.ScaleFactor;
|
int monitorWidth = monitor.Geometry.Width * monitor.ScaleFactor;
|
||||||
int monitorHeight = Display.PrimaryMonitor.Geometry.Height * Display.PrimaryMonitor.ScaleFactor;
|
int monitorHeight = monitor.Geometry.Height * monitor.ScaleFactor;
|
||||||
|
|
||||||
DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280;
|
DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280;
|
||||||
DefaultHeight = monitorHeight < 760 ? monitorHeight : 760;
|
DefaultHeight = monitorHeight < 760 ? monitorHeight : 760;
|
||||||
|
@@ -1749,11 +1749,11 @@
|
|||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="_expandRamToggle">
|
<object class="GtkCheckButton" id="_expandRamToggle">
|
||||||
<property name="label" translatable="yes">Expand DRAM Size to 6GiB</property>
|
<property name="label" translatable="yes">Use alternative memory layout (Developers)</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">False</property>
|
<property name="receives-default">False</property>
|
||||||
<property name="tooltip-text" translatable="yes">Increases the amount of memory on the emulated system from 4GiB to 6GiB. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure.</property>
|
<property name="tooltip-text" translatable="yes">Utilizes an alternative MemoryMode layout to mimic a Switch development model. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure.</property>
|
||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="margin-top">5</property>
|
<property name="margin-top">5</property>
|
||||||
<property name="margin-bottom">5</property>
|
<property name="margin-bottom">5</property>
|
||||||
|
Reference in New Issue
Block a user