Compare commits

..

26 Commits

Author SHA1 Message Date
gdkchan
f82309fa2d Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve (#3723)
* Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve

* FramebufferParams is no longer required there

* Implement Specialization Constants and merge CopyMS Shaders (#15)

* Vulkan: Initial Specialization Constants

* Replace with specialized helper shader

* Reimplement everything

Fix nonexistant interaction with Ryu pipeline caching
Decouple specialization info from data and relocate them
Generalize mapping and add type enum to better match spv types
Use local fixed scopes instead of global unmanaged allocs

* Fix misses in initial implementation

Use correct info variable in Create2DLayerView
Add ShaderStorageImageMultisample to required feature set

* Use texture for source image

* No point in using ReadOnlyMemory

* Apply formatting feedback

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Apply formatting suggestions on shader source

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Support conversion with samples count that does not match the requested count, other minor changes

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>
2022-11-02 18:17:19 -03:00
Mary-nyan
7d8e198c33 fix: Support FFmpeg 5.1.x for decoding (#3816)
For some reason FFmpeg 5.1.x reverted part of the changes made in 5.0.x
on AVCodec.

This fix decoding issues with it.
2022-11-02 09:26:50 +01:00
riperiperi
3d98e1361b GPU: Use a bitmap to track buffer modified flags. (#3775)
* Initial implementation

* Some improvements.

* Fix incorrect cast

* Performance improvement and improved correctness

* Add very fast path when all handles are checked.

* Slightly faster

* Add comment

* De-virtualize region handle

All region handles are now bitmap backed.

* Remove non-bitmap tracking

* Remove unused methods

* Add docs, remove unused methods

* Address Feedback

* Rename file
2022-10-29 22:07:37 +00:00
TSRBerry
141cf61ff7 CI: Run git_short_hash inside of bash (#3808) 2022-10-29 19:00:08 +00:00
Wunk
3fe3598d41 Vulkan: Replace VK_EXT_debug_report usage with VK_EXT_debug_utils (#3802)
* Vulkan: Replace `VK_EXT_debug_report` usage with `VK_EXT_debug_utils`

[VK_EXT_debug_report](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_report.html)
has been depreciated for quite some time now in favor of the much more
featureful
[VK_EXT_debug_utils](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html)
extension.

This PR converts our debug-report-callback into the newer
debug-messenger pattern.

`VK_EXT_debug_utils` adds some additional diagnostic tooling for marking
debug-label scopes for queue-operations, command-buffers, and assigning
name-labels to vulkan objects to aid in debugging(for a later PR).

* Vulkan: Fix `DebugMessenger` severity-flag classification

Extension bits between the two flags, for reference:

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugUtilsMessageSeverityFlagBitsEXT.html

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugReportFlagBitsEXT.html
2022-10-29 14:09:25 -03:00
gdkchan
59cdf310bd SPIR-V: Fix tessellation control shader output types (#3807)
* SPIR-V: Fix tessellation control shader output types

* Shader cache version bump
2022-10-29 13:45:30 -03:00
dependabot[bot]
4e34170a84 nuget: bump System.IdentityModel.Tokens.Jwt from 6.15.0 to 6.25.0 (#3806)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.25.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-29 10:46:46 +02:00
Luke Warner
d540af5dc0 AppletAE: stub SetRecordVolumeMuted (#3804)
* Update IIrSensorServer.cs

* Update IIrSensorServer.cs

* Apply suggestions from code review

Addressed formatting feedback

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update IIrSensorServer.cs

* Update ISelfController.cs

* Update ISelfController.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-10-27 23:15:57 +00:00
Luke Warner
f7c7b66fc0 hid/irs: Stub StopImageProcessorAsync (#3799)
* Update IIrSensorServer.cs

* Update IIrSensorServer.cs

* Apply suggestions from code review

Addressed formatting feedback

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update IIrSensorServer.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-10-27 21:36:37 +00:00
gdkchan
28ba55598d Vulkan: Fix indirect buffer barrier (#3798) 2022-10-26 14:53:11 -03:00
riperiperi
9719b6a112 Vulkan: Use dynamic state for blend constants (#3793) 2022-10-25 23:49:23 +00:00
VocalFan
f70236f947 Updated Compatibility info and GPU info (Vulkan, SPIRV, and Texture Recompression) (#3568)
* Updated Compatibility info and GPU info (Vulkan, SPIRV, and Texture Recompression)

* Added Mutant's changes.

Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com>

* Five to four.

* Fixed github's terrible conflict diffs

Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com>
2022-10-24 16:40:39 +00:00
merry
eafadf10c7 Ryujinx.Tests.Unicorn: Implement IDisposable (#3794)
Dispose unicorn when done
2022-10-23 23:51:54 +00:00
Mary-nyan
9b06ee7736 Attempt to fix issues since github-script v6 upgrade 2022-10-23 17:15:15 +02:00
Emmanuel Hansen
baba2c2467 Avalonia: Use overlay dialog for controller applet (#3777)
* use overlay dialog for controller applet

* Update Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2022-10-23 11:15:45 +02:00
dependabot[bot]
286e5d39b2 nuget: bump SPB from 0.0.4-build24 to 0.0.4-build27 (#3791)
Bumps [SPB](https://github.com/Thog/SPB) from 0.0.4-build24 to 0.0.4-build27.
- [Release notes](https://github.com/Thog/SPB/releases)
- [Commits](https://github.com/Thog/SPB/commits)

---
updated-dependencies:
- dependency-name: SPB
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-22 10:32:12 +02:00
TSRBerry
dc529c1181 ci: Add updates for nuget packages to dependabot (#3786) 2022-10-21 14:23:16 +02:00
TSRBerry
c7cf1cbc35 CI: Update workflows (#3774)
* ci: Update workflows

* gha: Add no-build switch to test action
2022-10-21 10:31:38 +02:00
TSRBerry
d8e487d018 gha: Add dependabot.yml (#3778) 2022-10-21 10:16:28 +02:00
gdkchan
5fdc46ac7f Vulkan: Fix vertex position Z conversion with geometry shader passthrough (#3781)
* Vulkan: Fix vertex position Z conversion with geometry shader passthrough

* Shader cache version bump
2022-10-21 04:48:21 +00:00
Mortus06
1e5b45f580 Avalonia: update it_IT.json (#3742)
* Avalonia: update it_IT.json

* Fixed ; instead of :

* Update it_IT.json

port di #3766

* Update Ryujinx.Ava/Assets/Locales/it_IT.json

Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com>

* Grammar fix

Co-authored-by: Lorenzo Giannini <55211569+Lorenzo0310200@users.noreply.github.com>
Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com>
2022-10-19 09:30:28 +00:00
LDj3SNuD
62585755fd Do not clear the rejit queue when overlaps count is equal to 0. (#3721)
* Do not clear the rejit queue when overlaps count is equal to 0.

* Ptc and PtcProfiler must be invalidated.

* Revert "Ptc and PtcProfiler must be invalidated."

This reverts commit f5b0ad9d7d.

* Fix #3710 slow path due to #3701.
2022-10-19 02:08:34 +00:00
WilliamWsyHK
56621615b1 Implement the GetSessionCacheMode in SSL servuce (#3735) 2022-10-19 01:27:11 +00:00
Carl Ouellette
2099a3e84b Manage state of NfcManager (#3678)
* Manage state of NfcManager

Very basic state management but works with Hyrule Warriors Definitive Edition. Partially fixes #2122

* Fixes changes from review
2022-10-19 01:14:31 +00:00
gdkchan
7d26e4ac7b Fix mapping leaks caused by UnmapView not working on Linux (#3650)
* Add test for UnmapView mapping leaks

* Throw when UnmapView fails on Linux

* Fix UnmapView

* Remove throw
2022-10-19 01:02:45 +00:00
merry
8d41402fa6 A32: Implement VCVTT, VCVTB (#3710)
* A32: Implement VCVTT, VCVTB

* A32: F16C implementation of VCVTT/VCVTB
2022-10-19 02:36:04 +02:00
58 changed files with 2386 additions and 467 deletions

24
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
labels:
- "infra"
reviewers:
- marysaka
commit-message:
prefix: "ci"
- package-ecosystem: nuget
directory: /
open-pull-requests-limit: 5
schedule:
interval: daily
labels:
- "infra"
reviewers:
- marysaka
commit-message:
prefix: nuget

View File

@@ -48,21 +48,22 @@ jobs:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1.0"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1
- name: Get git short hash
id: git_short_hash
run: echo "::set-output name=result::$(git rev-parse --short "${{ github.sha }}")"
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Clear
run: dotnet clean && dotnet nuget locals all --clear
- 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
- name: Test
run: dotnet test -c "${{ matrix.configuration }}"
run: dotnet test --no-build -c "${{ matrix.configuration }}"
- 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
if: github.event_name == 'pull_request'
@@ -73,19 +74,19 @@ jobs:
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
if: github.event_name == 'pull_request'
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish
if: github.event_name == 'pull_request'
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_sdl2_headless
if: github.event_name == 'pull_request'
- name: Upload Ryujinx.Ava artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_ava

View File

@@ -8,7 +8,7 @@ jobs:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v3
- uses: actions/github-script@v6
with:
script: |
const {owner, repo} = context.repo;
@@ -16,7 +16,7 @@ jobs:
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const issue_number = await (async () => {
const pulls = await github.pulls.list({owner, repo});
const pulls = await github.rest.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === pull_head_sha) {
@@ -31,7 +31,7 @@ jobs:
return core.error(`No matching pull request found`);
}
const {data: {artifacts}} = await github.actions.listWorkflowRunArtifacts({owner, repo, run_id});
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
@@ -57,12 +57,12 @@ jobs:
body += hidden_headless_artifacts;
body += hidden_debug_artifacts;
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
} else {
core.info(`Creating a comment`);
await github.issues.createComment({repo, owner, issue_number, body});
await github.rest.issues.createComment({repo, owner, issue_number, body});
}

View File

@@ -25,8 +25,8 @@ jobs:
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: Ensure NuGet Source
@@ -36,8 +36,8 @@ jobs:
- name: Get version info
id: version_info
run: |
echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}"
echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")"
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Configure for release
run: |

View File

@@ -0,0 +1,44 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdCvtTB : OpCode32, IOpCode32Simd
{
public int Vd { get; }
public int Vm { get; }
public bool Op { get; } // Convert to Half / Convert from Half
public bool T { get; } // Top / Bottom
public int Size { get; } // Double / Single
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, false);
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, true);
public OpCode32SimdCvtTB(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
{
IsThumb = isThumb;
Op = ((opCode >> 16) & 0x1) != 0;
T = ((opCode >> 7) & 0x1) != 0;
Size = ((opCode >> 8) & 0x1);
RegisterSize = Size == 1 ? RegisterSize.Int64 : RegisterSize.Int32;
if (Size == 1)
{
if (Op)
{
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
else
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
}
}
else
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
}
}
}

View File

@@ -828,6 +828,7 @@ namespace ARMeilleure.Decoders
SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int.
SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32.
SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp).
SetVfp("<<<<11101x11001xxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_TB, OpCode32SimdCvtTB.Create, OpCode32SimdCvtTB.CreateT32);
SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32);
SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);

View File

@@ -261,6 +261,74 @@ namespace ARMeilleure.Instructions
}
}
public static void Vcvt_TB(ArmEmitterContext context)
{
OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp;
if (Optimizations.UseF16c)
{
Debug.Assert(!Optimizations.ForceLegacySse);
if (op.Op)
{
Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
if (op.Size == 1)
{
res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res);
}
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
res = context.VectorExtract16(res, 0);
InsertScalar16(context, op.Vd, op.T, res);
}
else
{
Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T));
res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res);
if (op.Size == 1)
{
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
}
res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0);
InsertScalar(context, op.Vd, res);
}
}
else
{
if (op.Op)
{
// Convert to half.
Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
MethodInfo method = op.Size == 1
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
context.StoreToContext();
Operand res = context.Call(method, src);
context.LoadFromContext();
InsertScalar16(context, op.Vd, op.T, res);
}
else
{
// Convert from half.
Operand src = ExtractScalar16(context, op.Vm, op.T);
MethodInfo method = op.Size == 1
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
context.StoreToContext();
Operand res = context.Call(method, src);
context.LoadFromContext();
InsertScalar(context, op.Vd, res);
}
}
}
// VRINTA/M/N/P (floating-point).
public static void Vrint_RM(ArmEmitterContext context)
{

View File

@@ -70,6 +70,22 @@ namespace ARMeilleure.Instructions
context.Copy(vec, insert);
}
public static Operand ExtractScalar16(ArmEmitterContext context, int reg, bool top)
{
return context.VectorExtract16(GetVecA32(reg >> 2), ((reg & 3) << 1) | (top ? 1 : 0));
}
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
{
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32);
Operand vec, insert;
vec = GetVecA32(reg >> 2);
insert = context.VectorInsert16(vec, value, ((reg & 3) << 1) | (top ? 1 : 0));
context.Copy(vec, insert);
}
public static Operand ExtractElement(ArmEmitterContext context, int reg, int size, bool signed)
{
return EmitVectorExtract32(context, reg >> (4 - size), reg & ((16 >> size) - 1), size, signed);

View File

@@ -455,13 +455,16 @@ namespace ARMeilleure.Translation
public void InvalidateJitCacheRegion(ulong address, ulong size)
{
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
ClearRejitQueue(allowRequeue: true);
ulong[] overlapAddresses = Array.Empty<ulong>();
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
if (overlapsCount != 0)
{
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
ClearRejitQueue(allowRequeue: true);
}
for (int index = 0; index < overlapsCount; index++)
{
ulong overlapAddress = overlapAddresses[index];

View File

@@ -36,8 +36,8 @@
## Compatibility
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable.
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
## Usage
@@ -90,7 +90,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
- **GPU**
The GPU emulator emulates the Switch's Maxwell GPU using the OpenGL API (version 4.5 minimum) through a custom build of OpenTK. There are currently four graphics enhancements available to the end user in Ryujinx: disk shader caching, resolution scaling, aspect ratio adjustment and anisotropic filtering. These enhancements can be adjusted or toggled as desired in the GUI.
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
- **Input**

View File

@@ -49,8 +49,8 @@
"GameListContextMenuManageTitleUpdatesToolTip": "Apre la finestra di gestione aggiornamenti del gioco",
"GameListContextMenuManageDlc": "Gestici DLC",
"GameListContextMenuManageDlcToolTip": "Apre la finestra di gestione DLC",
"GameListContextMenuOpenModsDirectory": "Apri cartella delle mods",
"GameListContextMenuOpenModsDirectoryToolTip": "Apre la cartella che contiene le mods dell'applicazione",
"GameListContextMenuOpenModsDirectory": "Apri cartella delle mod",
"GameListContextMenuOpenModsDirectoryToolTip": "Apre la cartella che contiene le mod dell'applicazione",
"GameListContextMenuCacheManagement": "Gestione della cache",
"GameListContextMenuCacheManagementPurgePptc": "Pulisci PPTC cache",
"GameListContextMenuCacheManagementPurgePptcToolTip": "Elimina la PPTC cache dell'applicazione",
@@ -81,7 +81,7 @@
"SettingsTabGeneralRemove": "Rimuovi",
"SettingsTabSystem": "Sistema",
"SettingsTabSystemCore": "Core",
"SettingsTabSystemSystemRegion": "Regione di sistema:",
"SettingsTabSystemSystemRegion": "Regione del sistema:",
"SettingsTabSystemSystemRegionJapan": "Giappone",
"SettingsTabSystemSystemRegionUSA": "Stati Uniti d'America",
"SettingsTabSystemSystemRegionEurope": "Europa",
@@ -89,7 +89,7 @@
"SettingsTabSystemSystemRegionChina": "Cina",
"SettingsTabSystemSystemRegionKorea": "Corea",
"SettingsTabSystemSystemRegionTaiwan": "Taiwan",
"SettingsTabSystemSystemLanguage": "Lingua di sistema:",
"SettingsTabSystemSystemLanguage": "Lingua del sistema:",
"SettingsTabSystemSystemLanguageJapanese": "Giapponese",
"SettingsTabSystemSystemLanguageAmericanEnglish": "Inglese americano",
"SettingsTabSystemSystemLanguageFrench": "Francese",
@@ -107,8 +107,8 @@
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Spagnolo latino americano",
"SettingsTabSystemSystemLanguageSimplifiedChinese": "Cinese semplificato",
"SettingsTabSystemSystemLanguageTraditionalChinese": "Cinese tradizionale",
"SettingsTabSystemSystemTimeZone": "Fuso orario di sistema:",
"SettingsTabSystemSystemTime": "Data e ora di sistema:",
"SettingsTabSystemSystemTimeZone": "Fuso orario del sistema:",
"SettingsTabSystemSystemTime": "Data e ora del sistema:",
"SettingsTabSystemEnableVsync": "Attiva VSync",
"SettingsTabSystemEnablePptc": "Attiva PPTC (Profiled Persistent Translation Cache)",
"SettingsTabSystemEnableFsIntegrityChecks": "Attiva controlli d'integrità FS",
@@ -155,7 +155,7 @@
"SettingsTabLoggingEnableTraceLogs": "Attiva Trace Logs",
"SettingsTabLoggingEnableGuestLogs": "Attiva Guest Logs",
"SettingsTabLoggingEnableFsAccessLogs": "Attiva Fs Access Logs",
"SettingsTabLoggingFsGlobalAccessLogMode": "Modalità di log accesso globale Fs:",
"SettingsTabLoggingFsGlobalAccessLogMode": "Modalità log accesso globale Fs:",
"SettingsTabLoggingDeveloperOptions": "Opzioni da sviluppatore (AVVISO: Ridurrà le prestazioni)",
"SettingsTabLoggingOpenglLogLevel": "Livello di log OpenGL:",
"SettingsTabLoggingOpenglLogLevelNone": "Nessuno",
@@ -240,7 +240,7 @@
"ControllerSettingsRightSR": "SR",
"ControllerSettingsExtraButtonsLeft": "Tasto sinitro",
"ControllerSettingsExtraButtonsRight": "Tasto destro",
"ControllerSettingsMisc": "Miscellanee",
"ControllerSettingsMisc": "Varie",
"ControllerSettingsTriggerThreshold": "Sensibilità dei grilletti:",
"ControllerSettingsMotion": "Movimento",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usa sensore compatibile con CemuHook",
@@ -261,7 +261,7 @@
"UserProfilesClose": "Chiudi",
"ProfileImageSelectionTitle": "Selezione dell'immagine profilo",
"ProfileImageSelectionHeader": "Scegli un'immagine profilo",
"ProfileImageSelectionNote": "Puoi importare un'immagine profilo personalizzata o selezionare un avatar dal firmware di sistema",
"ProfileImageSelectionNote": "Puoi importare un'immagine profilo personalizzata o selezionare un avatar dal firmware del sistema",
"ProfileImageSelectionImportImage": "Importa file immagine",
"ProfileImageSelectionSelectAvatar": "Seleziona avatar dal firmware",
"InputDialogTitle": "Input Dialog",
@@ -293,7 +293,7 @@
"ControllerSettingsRumbleStrongMultiplier": "Moltiplicatore vibrazione forte",
"ControllerSettingsRumbleWeakMultiplier": "Moltiplicatore vibrazione debole",
"DialogMessageSaveNotAvailableMessage": "Non ci sono dati di salvataggio per {0} [{1:x16}]",
"DialogMessageSaveNotAvailableCreateSaveMessage": "Vuoi creare dei dat di salvataggio per questo gioco?",
"DialogMessageSaveNotAvailableCreateSaveMessage": "Vuoi creare dei dati di salvataggio per questo gioco?",
"DialogConfirmationTitle": "Ryujinx - Conferma",
"DialogUpdaterTitle": "Ryujinx - Updater",
"DialogErrorTitle": "Ryujinx - Errore",
@@ -318,7 +318,7 @@
"DialogUpdaterDownloadingMessage": "Download dell'aggiornamento...",
"DialogUpdaterExtractionMessage": "Estrazione dell'aggiornamento...",
"DialogUpdaterRenamingMessage": "Rinominazione dell'aggiornamento...",
"DialogUpdaterAddingFilesMessage": "Aggiunta nuovo aggiornamento...",
"DialogUpdaterAddingFilesMessage": "Aggiunta del nuovo aggiornamento...",
"DialogUpdaterCompleteMessage": "Aggiornamento completato!",
"DialogUpdaterRestartMessage": "Vuoi riavviare Ryujinx adesso?",
"DialogUpdaterArchNotSupportedMessage": "Non stai usando un'architettura di sistema supportata!",
@@ -331,7 +331,7 @@
"DialogThemeRestartMessage": "Il tema è stato salvato. E' richiesto un riavvio per applicare un tema.",
"DialogThemeRestartSubMessage": "Vuoi riavviare?",
"DialogFirmwareInstallEmbeddedMessage": "Vuoi installare il firmware incorporato in questo gioco? (Firmware {0})",
"DialogFirmwareInstallEmbeddedSuccessMessage": "Non è stato trovato alcun firmware installato, ma Ryujinx è riuscito di installare il firmware {0} dal gioco fornito.\nL'emulatore si avvierà adesso.",
"DialogFirmwareInstallEmbeddedSuccessMessage": "Non è stato trovato alcun firmware installato, ma Ryujinx è riuscito ad installare il firmware {0} dal gioco fornito.\nL'emulatore si avvierà adesso.",
"DialogFirmwareNoFirmwareInstalledMessage": "Nessun firmware installato",
"DialogFirmwareInstalledMessage": "Il firmware {0} è stato installato",
"DialogOpenSettingsWindowLabel": "Apri finestra delle impostazioni",
@@ -355,14 +355,14 @@
"DialogShaderDeletionMessage": "Stai per eliminare la Shader cache per :\n\n{0}\n\nSei sicuro di voler proseguire?",
"DialogShaderDeletionErrorMessage": "Errore nell'eliminazione della Shader cache a {0}: {1}",
"DialogRyujinxErrorMessage": "Ryujinx ha incontrato un errore",
"DialogInvalidTitleIdErrorMessage": "Errore UI: Il gioco selezionato non ha un title ID valido",
"DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Un firmware di sistema valido non è stato trovato in {0}.",
"DialogInvalidTitleIdErrorMessage": "Errore UI: Il gioco selezionato non ha un ID titolo valido",
"DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Un firmware del sistema valido non è stato trovato in {0}.",
"DialogFirmwareInstallerFirmwareInstallTitle": "Installa firmware {0}",
"DialogFirmwareInstallerFirmwareInstallMessage": "La versione di sistema {0} sarà installata.",
"DialogFirmwareInstallerFirmwareInstallMessage": "La versione del sistema {0} sarà installata.",
"DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nQuesta sostituirà l'attuale versione di sistema {0}.",
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nVuoi continuare?",
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installazione del firmware...",
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "La versione di sistema {0} è stata installata.",
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "La versione del sistema {0} è stata installata.",
"DialogUserProfileDeletionWarningMessage": "Non ci sarebbero altri profili da aprire se il profilo selezionato viene cancellato",
"DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?",
"DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.",
@@ -557,4 +557,40 @@
"SettingsXamlThemeFile" : "File del tema xaml",
"SettingsTabHotkeysResScaleUpHotkey": "Aumentare la risoluzione:",
"SettingsTabHotkeysResScaleDownHotkey": "Diminuire la risoluzione:"
"AvatarWindowTitle": "Gestisci account - Avatar"
"Amiibo": "Amiibo",
"Unknown": "Sconosciuto",
"Usage": "Utilizzo",
"Writable": "Scrivibile",
"SelectDlcDialogTitle": "Seleziona file dei DLC",
"SelectUpdateDialogTitle": "Seleziona file di aggiornamento",
"UserProfileWindowTitle": "Gestisci profili degli utenti",
"CheatWindowTitle": "Gestisci cheat dei giochi",
"DlcWindowTitle": "Gestisci DLC dei giochi",
"UpdateWindowTitle": "Gestisci aggiornamenti dei giochi",
"CheatWindowHeading": "Cheat disponibiili per {0} [{1}]",
"DlcWindowHeading": "DLC disponibili per {0} [{1}]",
"UserProfilesEditProfile": "Modifica selezionati",
"Cancel": "Annulla",
"Save": "Salva",
"Discard": "Scarta",
"UserProfilesSetProfileImage": "Imposta immagine profilo",
"UserProfileEmptyNameError": "È richiesto un nome",
"UserProfileNoImageError": "Dev'essere impostata un'immagine profilo",
"GameUpdateWindowHeading": "Aggiornamenti disponibili per {0} [{1}]",
"UserProfilesName": "Name:",
"UserProfilesUserId": "User Id:",
"SettingsTabGraphicsBackend": "Backend grafica",
"SettingsTabGraphicsBackendTooltip": "Backend grafica da usare",
"SettingsEnableTextureRecompression": "Abilita Ricompressione Texture",
"SettingsEnableTextureRecompressionTooltip": "Comprime alcune texture per ridurre l'utilizzo della VRAM.\n\nL'utilizzo è consigliato con GPU con meno di 4GB di VRAM.\n\nLascia su OFF se non sei sicuro.",
"SettingsTabGraphicsPreferredGpu": "GPU preferita",
"SettingsTabGraphicsPreferredGpuTooltip": "Seleziona la scheda grafica che verrà usata con la backend grafica Vulkan.\n\nNon influenza la GPU che userà OpenGL.\n\nImposta la GPU contrassegnata come \"dGPU\" se non sei sicuro. Se non ce n'è una, lascia intatta quest'impostazione.",
"SettingsAppRequiredRestartMessage": "È richiesto un riavvio di Ryujinx",
"SettingsGpuBackendRestartMessage": "Le impostazioni della backend grafica o della GPU sono state modificate. Questo richiederà un riavvio perché le modifiche siano applicate",
"SettingsGpuBackendRestartSubMessage": "Vuoi riavviare ora?",
"RyujinxUpdaterMessage": "Vuoi aggiornare Ryujinx all'ultima versione?",
"SettingsTabHotkeysVolumeUpHotkey": "Aumentare il volume:",
"SettingsTabHotkeysVolumeDownHotkey": "Diminuire il volume:",
"VolumeShort": "Vol"
}

View File

@@ -34,7 +34,7 @@
<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.KHR" Version="2.10.1" />
<PackageReference Include="SPB" Version="0.0.4-build24" />
<PackageReference Include="SPB" Version="0.0.4-build27" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
</ItemGroup>

View File

@@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
@@ -27,7 +28,10 @@ namespace Ryujinx.Ava.Ui.Controls
string secondaryButton,
string closeButton,
int iconSymbol,
UserResult primaryButtonResult = UserResult.Ok)
UserResult primaryButtonResult = UserResult.Ok,
ManualResetEvent deferResetEvent = null,
Func<Window, Task> doWhileDeferred = null,
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
{
UserResult result = UserResult.None;
@@ -110,12 +114,19 @@ namespace Ryujinx.Ava.Ui.Controls
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.No;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.Cancel;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
if (deferResetEvent != null)
{
contentDialog.PrimaryButtonClick += deferCloseAction;
}
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
overlay?.Close();
@@ -143,35 +154,20 @@ namespace Ryujinx.Ava.Ui.Controls
Func<Window, Task> doWhileDeferred = null)
{
bool startedDeferring = false;
UserResult result = UserResult.None;
ContentDialog contentDialog = new ContentDialog
{
Title = title,
PrimaryButtonText = primaryButton,
SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton,
Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol),
PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
}),
};
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
contentDialog.PrimaryButtonClick -= DeferClose;
result = UserResult.No;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{
contentDialog.PrimaryButtonClick -= DeferClose;
result = UserResult.Cancel;
});
contentDialog.PrimaryButtonClick += DeferClose;
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
return result;
return await ShowContentDialog(
title,
primaryText,
secondaryText,
primaryButton,
secondaryButton,
closeButton,
iconSymbol,
primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok,
deferResetEvent,
doWhileDeferred,
DeferClose);
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
@@ -180,7 +176,7 @@ namespace Ryujinx.Ava.Ui.Controls
return;
}
contentDialog.PrimaryButtonClick -= DeferClose;
sender.PrimaryButtonClick -= DeferClose;
startedDeferring = true;
@@ -188,7 +184,7 @@ namespace Ryujinx.Ava.Ui.Controls
result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
contentDialog.PrimaryButtonClick -= DeferClose;
sender.PrimaryButtonClick -= DeferClose;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() =>

View File

@@ -710,7 +710,12 @@ namespace Ryujinx.Graphics.Gpu.Image
break;
case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray:
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
// We don't support copy between multisample and non-multisample depth-stencil textures
// because there's no way to emulate that since most GPUs don't support writing a
// custom stencil value into the texture, among several other API limitations.
if ((rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) &&
!rhs.FormatInfo.Format.IsDepthOrStencil())
{
return TextureViewCompatibility.CopyOnly;
}

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3759;
private const uint CodeGenVersion = 3807;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -7,7 +7,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
{
unsafe class FFmpegContext : IDisposable
{
private readonly FFCodec.AVCodec_decode _decodeFrame;
private unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
private readonly AVCodec_decode _decodeFrame;
private static readonly FFmpegApi.av_log_set_callback_callback _logFunc;
private readonly AVCodec* _codec;
private AVPacket* _packet;
@@ -53,17 +55,17 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
// libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union.
if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24))
{
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodec*)_codec)->CodecCallback);
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodec<AVCodec>*)_codec)->CodecCallback);
}
// libavcodec 59.x changed AvCodec private API layout.
else if (avCodecMajorVersion == 59)
{
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec501>*)_codec)->Decode);
}
// libavcodec 58.x and lower
else
{
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodecLegacy>*)_codec)->Decode);
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
}
}

View File

@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr PrivClass;
public IntPtr Profiles;
public unsafe byte* WrapperName;
public IntPtr ChLayouts;
#pragma warning restore CS0649
}
}

View File

@@ -2,7 +2,7 @@
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{
struct AVCodecLegacy
struct AVCodec501
{
#pragma warning disable CS0649
public unsafe byte* Name;
@@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr PrivClass;
public IntPtr Profiles;
public unsafe byte* WrapperName;
public IntPtr ChLayouts;
#pragma warning restore CS0649
}
}

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr AvClass;
public int LogLevelOffset;
public int CodecType;
public unsafe AVCodecLegacy* Codec;
public unsafe AVCodec* Codec;
public AVCodecID CodecId;
public uint CodecTag;
public IntPtr PrivData;

View File

@@ -2,12 +2,10 @@
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{
struct FFCodec
struct FFCodec<T> where T: struct
{
public unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
#pragma warning disable CS0649
public AVCodec Base;
public T Base;
public int CapsInternalOrCbType;
public int PrivDataSize;
public IntPtr UpdateThreadContext;

View File

@@ -262,6 +262,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Instruction ioVariable, elemIndex;
Instruction invocationId = null;
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
{
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
}
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (isUserAttr &&
@@ -273,7 +280,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4);
if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr))
bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
if (invocationId != null && isArray)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
}
else if (invocationId != null)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
}
else if (isArray)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
}
@@ -307,13 +324,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
bool isIndexed = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr));
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
{
if (invocationId != null)
{
return isIndexed
? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index)
: AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId);
}
else
{
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable;
}
}
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
if (isIndexed)
if (invocationId != null && isIndexed)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex);
}
else if (invocationId != null)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex);
}
else if (isIndexed)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex);
}
@@ -327,12 +361,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
Instruction invocationId = null;
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
{
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
}
elemType = AggregateType.FP32;
var ioVariable = isOutAttr ? OutputsArray : InputsArray;
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3));
if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr))
bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
if (invocationId != null && isArray)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
}
else if (invocationId != null)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
}
else if (isArray)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
}

View File

@@ -473,6 +473,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
if (context.Config.Stage == ShaderStage.TessellationControl)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(StorageClass.Output, attrType);
var spvVar = context.Variable(spvType, StorageClass.Output);
@@ -543,6 +548,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass);
@@ -634,6 +644,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
}
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass);

View File

@@ -37,7 +37,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Config = config;
if (config.GpPassthrough)
if (config.Stage == ShaderStage.TessellationControl)
{
// Required to index outputs.
Info.Inputs.Add(AttributeConsts.InvocationId);
}
else if (config.GpPassthrough)
{
int passthroughAttributes = config.PassthroughAttributes;
while (passthroughAttributes != 0)

View File

@@ -306,7 +306,10 @@ namespace Ryujinx.Graphics.Shader.Translation
config._perPatchAttributeLocations = locationsMap;
}
if (config.Stage != ShaderStage.Fragment)
// We don't consider geometry shaders using the geometry shader passthrough feature
// as being the last because when this feature is used, it can't actually modify any of the outputs,
// so the stage that comes before it is the last one that can do modifications.
if (config.Stage != ShaderStage.Fragment && (config.Stage != ShaderStage.Geometry || !config.GpPassthrough))
{
LastInVertexPipeline = false;
}

View File

@@ -12,13 +12,12 @@ namespace Ryujinx.Graphics.Vulkan
private const int MaxUpdateBufferSize = 0x10000;
public const AccessFlags DefaultAccessFlags =
AccessFlags.AccessIndirectCommandReadBit |
AccessFlags.AccessShaderReadBit |
AccessFlags.AccessShaderWriteBit |
AccessFlags.AccessTransferReadBit |
AccessFlags.AccessTransferWriteBit |
AccessFlags.AccessUniformReadBit |
AccessFlags.AccessShaderReadBit |
AccessFlags.AccessShaderWriteBit;
AccessFlags.AccessUniformReadBit;
private readonly VulkanRenderer _gd;
private readonly Device _device;

View File

@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClear;
private readonly IProgram _programStrideChange;
private readonly IProgram _programColorCopyBetweenMsNonMs;
public HelperShader(VulkanRenderer gd, Device device)
{
@@ -73,6 +74,20 @@ namespace Ryujinx.Graphics.Vulkan
{
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
var colorCopyMSBindings = new ShaderBindings(
new[] { 0 },
Array.Empty<int>(),
new[] { 0 },
new[] { 0 });
_programColorCopyBetweenMsNonMs = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorCopyBetweenMsNonMs, colorCopyMSBindings, ShaderStage.Compute, TargetLanguage.Spirv),
}, new[]
{
new SpecDescription((0, SpecConstType.Int32))
});
}
public void Blit(
@@ -136,11 +151,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
_pipeline.SetUniformBuffers(1, bufferRanges);
_pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
@@ -203,11 +214,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize);
_pipeline.SetUniformBuffers(1, bufferRanges);
_pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
@@ -269,11 +276,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
pipeline.SetUniformBuffers(1, bufferRanges);
pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
@@ -351,11 +354,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetCommandBuffer(cbs);
Span<BufferRange> cbRanges = stackalloc BufferRange[1];
cbRanges[0] = new BufferRange(bufferHandle, 0, ParamsBufferSize);
_pipeline.SetUniformBuffers(0, cbRanges);
_pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
@@ -480,12 +479,207 @@ namespace Ryujinx.Graphics.Vulkan
convertedCount * outputIndexSize);
}
public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
{
CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, src.Info.Samples, dst.Info.Width, dst.Info.Height);
}
public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
{
CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, dst.Info.Samples, src.Info.Width, src.Info.Height);
}
private void CopyMS(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
TextureView dst,
int srcLayer,
int dstLayer,
int depth,
int samples,
int nonMSWidth,
int nonMSHeight)
{
const int ParamsBufferSize = 16;
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
// X and Y are the expected texture samples.
// Z and W are the actual texture samples used.
// They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags((uint)samples));
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
TextureView.InsertImageBarrier(
gd.Api,
cbs.CommandBuffer,
src.GetImage().Get(cbs).Value,
TextureStorage.DefaultAccessMask,
AccessFlags.AccessShaderReadBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageComputeShaderBit,
ImageAspectFlags.ImageAspectColorBit,
src.FirstLayer + srcLayer,
src.FirstLevel,
depth,
1);
_pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(_programColorCopyBetweenMsNonMs);
var format = GetFormat(src.Info.BytesPerPixel);
int dispatchX = (nonMSWidth + 31) / 32;
int dispatchY = (nonMSHeight + 31) / 32;
// Specialize shader.
bool srcIsMs = src.Info.Target.IsMultisample();
int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel;
_pipeline.Specialize(conversionType);
_pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
if (src.Info.Target == Target.Texture2DMultisampleArray ||
dst.Info.Target == Target.Texture2DMultisampleArray)
{
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, format);
var dstView = Create2DLayerView(dst, dstLayer + z);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
srcView.Release();
dstView.Release();
}
}
else
{
var srcView = Create2DLayerView(src, srcLayer, format);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dst, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
srcView.Release();
}
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs);
TextureView.InsertImageBarrier(
gd.Api,
cbs.CommandBuffer,
dst.GetImage().Get(cbs).Value,
AccessFlags.AccessShaderWriteBit,
TextureStorage.DefaultAccessMask,
PipelineStageFlags.PipelineStageComputeShaderBit,
PipelineStageFlags.PipelineStageAllCommandsBit,
ImageAspectFlags.ImageAspectColorBit,
dst.FirstLayer + dstLayer,
dst.FirstLevel,
depth,
1);
}
private static (int, int) GetSampleCountXYLog2(int samples)
{
int samplesInXLog2 = 0;
int samplesInYLog2 = 0;
switch (samples)
{
case 2: // 2x1
samplesInXLog2 = 1;
break;
case 4: // 2x2
samplesInXLog2 = 1;
samplesInYLog2 = 1;
break;
case 8: // 4x2
samplesInXLog2 = 2;
samplesInYLog2 = 1;
break;
case 16: // 4x4
samplesInXLog2 = 2;
samplesInYLog2 = 2;
break;
case 32: // 8x4
samplesInXLog2 = 3;
samplesInYLog2 = 2;
break;
case 64: // 8x8
samplesInXLog2 = 3;
samplesInYLog2 = 3;
break;
}
return (samplesInXLog2, samplesInYLog2);
}
private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null)
{
var target = from.Info.Target switch
{
Target.Texture1DArray => Target.Texture1D,
Target.Texture2DArray => Target.Texture2D,
Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
_ => from.Info.Target
};
var info = new TextureCreateInfo(
from.Info.Width,
from.Info.Height,
from.Info.Depth,
1,
from.Info.Samples,
from.Info.BlockWidth,
from.Info.BlockHeight,
from.Info.BytesPerPixel,
format ?? from.Info.Format,
from.Info.DepthStencilMode,
target,
from.Info.SwizzleR,
from.Info.SwizzleG,
from.Info.SwizzleB,
from.Info.SwizzleA);
return from.CreateView(info, layer, 0);
}
private static GAL.Format GetFormat(int bytesPerPixel)
{
return bytesPerPixel switch
{
1 => GAL.Format.R8Uint,
2 => GAL.Format.R16Uint,
4 => GAL.Format.R32Uint,
8 => GAL.Format.R32G32Uint,
16 => GAL.Format.R32G32B32A32Uint,
_ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
};
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_programColorBlitClearAlpha.Dispose();
_programColorBlit.Dispose();
_programColorClear.Dispose();
_programStrideChange.Dispose();
_programColorCopyBetweenMsNonMs.Dispose();
_samplerNearest.Dispose();
_samplerLinear.Dispose();
_pipeline.Dispose();

View File

@@ -3,6 +3,8 @@ using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
@@ -228,10 +230,26 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
}
public void CommandBufferBarrier()
public unsafe void CommandBufferBarrier()
{
// TODO: More specific barrier?
Barrier();
MemoryBarrier memoryBarrier = new MemoryBarrier()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = BufferHolder.DefaultAccessFlags,
DstAccessMask = AccessFlags.AccessIndirectCommandReadBit
};
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageDrawIndirectBit,
0,
1,
memoryBarrier,
0,
null,
0,
null);
}
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@@ -535,10 +553,11 @@ namespace Ryujinx.Graphics.Vulkan
vkBlend = new PipelineColorBlendAttachmentState();
}
_newState.BlendConstantR = blend.BlendConstant.Red;
_newState.BlendConstantG = blend.BlendConstant.Green;
_newState.BlendConstantB = blend.BlendConstant.Blue;
_newState.BlendConstantA = blend.BlendConstant.Alpha;
DynamicState.SetBlendConstants(
blend.BlendConstant.Red,
blend.BlendConstant.Green,
blend.BlendConstant.Blue,
blend.BlendConstant.Alpha);
SignalStateChange();
}
@@ -680,6 +699,18 @@ namespace Ryujinx.Graphics.Vulkan
SignalStateChange();
}
public void Specialize<T>(in T data) where T : unmanaged
{
var dataSpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in data), 1));
if (!dataSpan.SequenceEqual(_newState.SpecializationData.Span))
{
_newState.SpecializationData = new SpecData(dataSpan);
SignalStateChange();
}
}
protected virtual void SignalAttachmentChange()
{
}
@@ -1171,14 +1202,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
var subpassDependency = new SubpassDependency(
0,
0,
PipelineStageFlags.PipelineStageAllGraphicsBit,
PipelineStageFlags.PipelineStageAllGraphicsBit,
AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessColorAttachmentWriteBit,
AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessShaderReadBit,
0);
var subpassDependency = PipelineConverter.CreateSubpassDependency();
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{

View File

@@ -6,6 +6,9 @@ namespace Ryujinx.Graphics.Vulkan
{
static class PipelineConverter
{
private const AccessFlags SubpassSrcAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessColorAttachmentWriteBit;
private const AccessFlags SubpassDstAccessMask = AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit | AccessFlags.AccessShaderReadBit;
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
{
const int MaxAttachments = Constants.MaxRenderTargets + 1;
@@ -100,14 +103,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
var subpassDependency = new SubpassDependency(
0,
0,
PipelineStageFlags.PipelineStageAllGraphicsBit,
PipelineStageFlags.PipelineStageAllGraphicsBit,
AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit,
AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit,
0);
var subpassDependency = CreateSubpassDependency();
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@@ -128,6 +124,32 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public static SubpassDependency CreateSubpassDependency()
{
return new SubpassDependency(
0,
0,
PipelineStageFlags.PipelineStageAllGraphicsBit,
PipelineStageFlags.PipelineStageAllGraphicsBit,
SubpassSrcAccessMask,
SubpassDstAccessMask,
0);
}
public unsafe static SubpassDependency2 CreateSubpassDependency2()
{
return new SubpassDependency2(
StructureType.SubpassDependency2,
null,
0,
0,
PipelineStageFlags.PipelineStageAllGraphicsBit,
PipelineStageFlags.PipelineStageAllGraphicsBit,
SubpassSrcAccessMask,
SubpassDstAccessMask,
0);
}
public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd)
{
PipelineState pipeline = new PipelineState();
@@ -135,11 +157,6 @@ namespace Ryujinx.Graphics.Vulkan
// It is assumed that Dynamic State is enabled when this conversion is used.
pipeline.BlendConstantA = state.BlendDescriptors[0].BlendConstant.Alpha;
pipeline.BlendConstantB = state.BlendDescriptors[0].BlendConstant.Blue;
pipeline.BlendConstantG = state.BlendDescriptors[0].BlendConstant.Green;
pipeline.BlendConstantR = state.BlendDescriptors[0].BlendConstant.Red;
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.CullModeNone;
pipeline.DepthBoundsTestEnable = false; // Not implemented.

View File

@@ -19,21 +19,34 @@ namespace Ryujinx.Graphics.Vulkan
private uint _frontWriteMask;
private uint _frontReference;
private Array4<float> _blendConstants;
public int ViewportsCount;
public Array16<Viewport> Viewports;
private enum DirtyFlags
{
None = 0,
DepthBias = 1 << 0,
Scissor = 1 << 1,
Stencil = 1 << 2,
Viewport = 1 << 3,
All = DepthBias | Scissor | Stencil | Viewport
Blend = 1 << 0,
DepthBias = 1 << 1,
Scissor = 1 << 2,
Stencil = 1 << 3,
Viewport = 1 << 4,
All = Blend | DepthBias | Scissor | Stencil | Viewport
}
private DirtyFlags _dirty;
public void SetBlendConstants(float r, float g, float b, float a)
{
_blendConstants[0] = r;
_blendConstants[1] = g;
_blendConstants[2] = b;
_blendConstants[3] = a;
_dirty |= DirtyFlags.Blend;
}
public void SetDepthBias(float slopeFactor, float constantFactor, float clamp)
{
_depthBiasSlopeFactor = slopeFactor;
@@ -87,6 +100,11 @@ namespace Ryujinx.Graphics.Vulkan
public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer)
{
if (_dirty.HasFlag(DirtyFlags.Blend))
{
RecordBlend(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.DepthBias))
{
RecordDepthBias(api, commandBuffer);
@@ -110,6 +128,11 @@ namespace Ryujinx.Graphics.Vulkan
_dirty = DirtyFlags.None;
}
private void RecordBlend(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetBlendConstants(commandBuffer, _blendConstants.AsSpan());
}
private void RecordDepthBias(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor);

View File

@@ -312,6 +312,7 @@ namespace Ryujinx.Graphics.Vulkan
public NativeArray<PipelineShaderStageCreateInfo> Stages;
public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes;
public PipelineLayout PipelineLayout;
public SpecData SpecializationData;
public void Initialize()
{
@@ -334,7 +335,7 @@ namespace Ryujinx.Graphics.Vulkan
ShaderCollection program,
PipelineCache cache)
{
if (program.TryGetComputePipeline(out var pipeline))
if (program.TryGetComputePipeline(ref SpecializationData, out var pipeline))
{
return pipeline;
}
@@ -354,20 +355,36 @@ namespace Ryujinx.Graphics.Vulkan
Pipeline pipelineHandle = default;
bool hasSpec = program.SpecDescriptions != null;
var desc = hasSpec ? program.SpecDescriptions[0] : SpecDescription.Empty;
if (hasSpec && SpecializationData.Length < (int)desc.Info.DataSize)
{
throw new InvalidOperationException("Specialization data size does not match description");
}
fixed (SpecializationInfo* info = &desc.Info)
fixed (SpecializationMapEntry* map = desc.Map)
fixed (byte* data = SpecializationData.Span)
{
if (hasSpec)
{
info->PMapEntries = map;
info->PData = data;
pipelineCreateInfo.Stage.PSpecializationInfo = info;
}
gd.Api.CreateComputePipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError();
}
pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
program.AddComputePipeline(pipeline);
program.AddComputePipeline(ref SpecializationData, pipeline);
return pipeline;
}
public unsafe void DestroyComputePipeline(ShaderCollection program)
{
program.RemoveComputePipeline();
}
public unsafe Auto<DisposablePipeline> CreateGraphicsPipeline(
VulkanRenderer gd,
Device device,
@@ -499,7 +516,7 @@ namespace Ryujinx.Graphics.Vulkan
colorBlendState.BlendConstants[3] = BlendConstantA;
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
int dynamicStatesCount = supportsExtDynamicState ? 9 : 8;
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
@@ -510,10 +527,11 @@ namespace Ryujinx.Graphics.Vulkan
dynamicStates[4] = DynamicState.StencilCompareMask;
dynamicStates[5] = DynamicState.StencilWriteMask;
dynamicStates[6] = DynamicState.StencilReference;
dynamicStates[7] = DynamicState.BlendConstants;
if (supportsExtDynamicState)
{
dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
dynamicStates[8] = DynamicState.VertexInputBindingStrideExt;
}
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo()

View File

@@ -26,6 +26,8 @@ namespace Ryujinx.Graphics.Vulkan
public ProgramLinkStatus LinkStatus { get; private set; }
public readonly SpecDescription[] SpecDescriptions;
public bool IsLinked
{
get
@@ -40,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan
}
private HashTableSlim<PipelineUid, Auto<DisposablePipeline>> _graphicsPipelineCache;
private Auto<DisposablePipeline> _computePipeline;
private HashTableSlim<SpecData, Auto<DisposablePipeline>> _computePipelineCache;
private VulkanRenderer _gd;
private Device _device;
@@ -52,17 +54,24 @@ namespace Ryujinx.Graphics.Vulkan
private Task _compileTask;
private bool _firstBackgroundUse;
public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, bool isMinimal = false)
public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false)
{
_gd = gd;
_device = device;
if (specDescription != null && specDescription.Length != shaders.Length)
{
throw new ArgumentException($"{nameof(specDescription)} array length must match {nameof(shaders)} array if provided");
}
gd.Shaders.Add(this);
var internalShaders = new Shader[shaders.Length];
_infos = new PipelineShaderStageCreateInfo[shaders.Length];
SpecDescriptions = specDescription;
LinkStatus = ProgramLinkStatus.Incomplete;
uint stages = 0;
@@ -314,14 +323,9 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
public void AddComputePipeline(Auto<DisposablePipeline> pipeline)
public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline)
{
_computePipeline = pipeline;
}
public void RemoveComputePipeline()
{
_computePipeline = null;
(_computePipelineCache ??= new()).Add(ref key, pipeline);
}
public void AddGraphicsPipeline(ref PipelineUid key, Auto<DisposablePipeline> pipeline)
@@ -329,10 +333,20 @@ namespace Ryujinx.Graphics.Vulkan
(_graphicsPipelineCache ??= new()).Add(ref key, pipeline);
}
public bool TryGetComputePipeline(out Auto<DisposablePipeline> pipeline)
public bool TryGetComputePipeline(ref SpecData key, out Auto<DisposablePipeline> pipeline)
{
pipeline = _computePipeline;
return pipeline != null;
if (_computePipelineCache == null)
{
pipeline = default;
return false;
}
if (_computePipelineCache.TryGetValue(ref key, out pipeline))
{
return true;
}
return false;
}
public bool TryGetGraphicsPipeline(ref PipelineUid key, out Auto<DisposablePipeline> pipeline)
@@ -390,7 +404,14 @@ namespace Ryujinx.Graphics.Vulkan
}
}
_computePipeline?.Dispose();
if (_computePipelineCache != null)
{
foreach (Auto<DisposablePipeline> pipeline in _computePipelineCache.Values)
{
pipeline.Dispose();
}
}
if (_dummyRenderPass.Value.Handle != 0)
{
_dummyRenderPass.Dispose();

View File

@@ -0,0 +1,89 @@
#version 450 core
// +ve for MsToNonMs, -ve for reverse
layout (constant_id = 0) const int convType = 0;
layout (std140, binding = 0) uniform sample_counts_log2_in
{
ivec4 sample_counts_log2;
};
#define R8_ID 1
#define R16_ID 2
#define R32_ID 4
#define RG32_ID 8
#define RGBA32_ID 16
#define R8_TYPE r8ui
#define R16_TYPE r16ui
#define R32_TYPE r32ui
#define RG32_TYPE rg32ui
#define RGBA32_TYPE rgba32ui
#define DECLARE_BINDINGS(type) layout (set = 3, binding = 0, type##_TYPE) uniform uimage2DMS dstMS ## type; \
layout (set = 3, binding = 0, type##_TYPE) uniform uimage2D dst ## type;
#define CASE_SIZE(type) case type##_ID: imageSz = imageSize(dst ## type); break;
#define CASE_CONVERT(type) case type##_ID: imageStore(dst ## type, ivec2(coords), texelFetch(srcMS, shiftedCoords, sampleIdx)); break; \
case -type##_ID: imageStore(dstMS ## type, shiftedCoords, sampleIdx, texelFetch(src, ivec2(coords), 0)); break;
// src tex
layout (set = 2, binding = 0) uniform usampler2DMS srcMS;
layout (set = 2, binding = 0) uniform usampler2D src;
// dst img
DECLARE_BINDINGS(R8)
DECLARE_BINDINGS(R16)
DECLARE_BINDINGS(R32)
DECLARE_BINDINGS(RG32)
DECLARE_BINDINGS(RGBA32)
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 imageSz = ivec2(0, 0);
switch (convType)
{
case 0: break;
CASE_SIZE(R8 )
CASE_SIZE(R16 )
CASE_SIZE(R32 )
CASE_SIZE(RG32 )
CASE_SIZE(RGBA32)
default:
imageSz = textureSize(src, 0);
break;
}
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
{
return;
}
int deltaX = sample_counts_log2.x - sample_counts_log2.z;
int deltaY = sample_counts_log2.y - sample_counts_log2.w;
int samplesInXLog2 = sample_counts_log2.z;
int samplesInYLog2 = sample_counts_log2.w;
int samplesInX = 1 << samplesInXLog2;
int samplesInY = 1 << samplesInYLog2;
int sampleIdx = ((int(coords.x) >> deltaX) & (samplesInX - 1)) | (((int(coords.y) >> deltaY) & (samplesInY - 1)) << samplesInXLog2);
samplesInXLog2 = sample_counts_log2.x;
samplesInYLog2 = sample_counts_log2.y;
ivec2 shiftedCoords = ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2);
switch (convType)
{
CASE_CONVERT(R8 )
CASE_CONVERT(R16 )
CASE_CONVERT(R32 )
CASE_CONVERT(RG32 )
CASE_CONVERT(RGBA32)
}
}

View File

@@ -551,5 +551,324 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x21, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
public static readonly byte[] ColorCopyBetweenMsNonMs = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2D, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x32, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47,
0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49,
0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6E, 0x76,
0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x52, 0x38, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x52, 0x31, 0x36, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x52, 0x33, 0x32, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x52, 0x47, 0x33, 0x32, 0x00, 0x05, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x52, 0x47, 0x42, 0x41, 0x33, 0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x40, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x00, 0x05, 0x00, 0x08, 0x00, 0x60, 0x00, 0x00, 0x00,
0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x73, 0x5F, 0x6C, 0x6F,
0x67, 0x32, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x60, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74,
0x73, 0x5F, 0x6C, 0x6F, 0x67, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x4D,
0x53, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D,
0x53, 0x52, 0x38, 0x00, 0x05, 0x00, 0x05, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D,
0x53, 0x52, 0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0xE7, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x4D, 0x53, 0x52, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0xFC, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D, 0x53, 0x52, 0x47, 0x33, 0x32, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x11, 0x01, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4D, 0x53, 0x52, 0x47, 0x42,
0x41, 0x33, 0x32, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1D, 0x01, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x19, 0x00, 0x09, 0x00, 0x25, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x25, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x31, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
0x37, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x19, 0x00, 0x09, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x02, 0x00, 0x46, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x53, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x5F, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0xB1, 0x00, 0x00, 0x00,
0xB0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xB1, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xBC, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xBC, 0x00, 0x00, 0x00,
0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0xD0, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0xD1, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
0xE5, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x19, 0x00, 0x09, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0x00,
0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x1C, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x1D, 0x01, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
0x1E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00,
0x1F, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00,
0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x0F, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x67, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x31, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x37, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x11, 0x00,
0x10, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x29, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x35, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00,
0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x46, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00,
0x46, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00,
0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x51, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
0x55, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0x2C, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x46, 0x00, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
0x46, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x5C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
0x5C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1E, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x63, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00,
0x62, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x63, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x6C, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x63, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00,
0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
0x6F, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x7D, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00,
0x83, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x88, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00,
0x82, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00,
0x8B, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x90, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x05, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x65, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00,
0x89, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0xA1, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x17, 0x00, 0x15, 0x00, 0x00, 0x00,
0xAC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xA3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0xA5, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF,
0xA7, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF,
0xA9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
0xAB, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x1F, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0xB1, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0xB0, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00,
0xB8, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xAD, 0x00, 0x00, 0x00,
0xAF, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xBB, 0x00, 0x00, 0x00,
0xBE, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00,
0xC1, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0xC3, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00,
0xC4, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00,
0xC5, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xBE, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0xC5, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x25, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0xB1, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0xB0, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00,
0xB8, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xC7, 0x00, 0x00, 0x00,
0xC9, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xD0, 0x00, 0x00, 0x00,
0xD3, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00,
0xD6, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0xD8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00,
0xD9, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA6, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x2B, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0xB1, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0xB0, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00,
0xB8, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xDC, 0x00, 0x00, 0x00,
0xDE, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00,
0xE8, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00,
0xEB, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0xED, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00,
0xEE, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00,
0xEF, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0xEF, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x31, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0xB1, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0xB0, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00,
0xB8, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xF1, 0x00, 0x00, 0x00,
0xF3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xFA, 0x00, 0x00, 0x00,
0xFD, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x02, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00,
0x04, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x04, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x37, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0xB1, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0xB0, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00,
0xB8, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0x06, 0x01, 0x00, 0x00,
0x08, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x01, 0x00, 0x00,
0x12, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x15, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x17, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x18, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB8, 0x00, 0x00, 0x00,
0x19, 0x01, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0x12, 0x01, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x19, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0xAC, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1E, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x01, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00,
};
}
}

View File

@@ -0,0 +1,103 @@
using Silk.NET.Vulkan;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
public enum SpecConstType
{
Bool32,
Int16,
Int32,
Int64,
Float16,
Float32,
Float64
}
sealed class SpecDescription
{
public readonly SpecializationInfo Info;
public readonly SpecializationMapEntry[] Map;
// For mapping a simple packed struct or single entry
public SpecDescription(params (uint Id, SpecConstType Type)[] description)
{
int count = description.Length;
Map = new SpecializationMapEntry[count];
uint structSize = 0;
for (int i = 0; i < count; ++i)
{
var typeSize = SizeOf(description[i].Type);
Map[i] = new SpecializationMapEntry(description[i].Id, structSize, typeSize);
structSize += typeSize;
}
Info = new SpecializationInfo()
{
DataSize = structSize,
MapEntryCount = (uint)count
};
}
// For advanced mapping with overlapping or staggered fields
public SpecDescription(SpecializationMapEntry[] map)
{
int count = map.Length;
Map = map;
uint structSize = 0;
for (int i = 0; i < count; ++i)
{
structSize = Math.Max(structSize, map[i].Offset + (uint)map[i].Size);
}
Info = new SpecializationInfo()
{
DataSize = structSize,
MapEntryCount = (uint)map.Length
};
}
private static uint SizeOf(SpecConstType type) => type switch
{
SpecConstType.Int16 or SpecConstType.Float16 => 2,
SpecConstType.Bool32 or SpecConstType.Int32 or SpecConstType.Float32 => 4,
SpecConstType.Int64 or SpecConstType.Float64 => 8,
_ => throw new ArgumentOutOfRangeException(nameof(type))
};
private SpecDescription()
{
Info = new();
}
public static readonly SpecDescription Empty = new();
}
readonly struct SpecData : IRefEquatable<SpecData>
{
private readonly byte[] _data;
private readonly int _hash;
public int Length => _data.Length;
public ReadOnlySpan<byte> Span => _data.AsSpan();
public override int GetHashCode() => _hash;
public SpecData(ReadOnlySpan<byte> data)
{
_data = new byte[data.Length];
data.CopyTo(_data);
var hc = new HashCode();
hc.AddBytes(data);
_hash = hc.ToHashCode();
}
public override bool Equals(object obj) => obj is SpecData other && Equals(other);
public bool Equals(ref SpecData other) => _data.AsSpan().SequenceEqual(other._data);
}
}

View File

@@ -355,5 +355,122 @@ namespace Ryujinx.Graphics.Vulkan
dstLayers,
levels);
}
public unsafe static void ResolveDepthStencil(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs,
TextureView src,
TextureView dst)
{
var dsAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 0, ImageLayout.General);
var dsResolveAttachmentReference = new AttachmentReference2(StructureType.AttachmentReference2, null, 1, ImageLayout.General);
var subpassDsResolve = new SubpassDescriptionDepthStencilResolve()
{
SType = StructureType.SubpassDescriptionDepthStencilResolve,
PDepthStencilResolveAttachment = &dsResolveAttachmentReference,
DepthResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit,
StencilResolveMode = ResolveModeFlags.ResolveModeSampleZeroBit
};
var subpass = new SubpassDescription2()
{
SType = StructureType.SubpassDescription2,
PipelineBindPoint = PipelineBindPoint.Graphics,
PDepthStencilAttachment = &dsAttachmentReference,
PNext = &subpassDsResolve
};
AttachmentDescription2[] attachmentDescs = new AttachmentDescription2[2];
attachmentDescs[0] = new AttachmentDescription2(
StructureType.AttachmentDescription2,
null,
0,
src.VkFormat,
TextureStorage.ConvertToSampleCountFlags((uint)src.Info.Samples),
AttachmentLoadOp.Load,
AttachmentStoreOp.Store,
AttachmentLoadOp.Load,
AttachmentStoreOp.Store,
ImageLayout.General,
ImageLayout.General);
attachmentDescs[1] = new AttachmentDescription2(
StructureType.AttachmentDescription2,
null,
0,
dst.VkFormat,
TextureStorage.ConvertToSampleCountFlags((uint)dst.Info.Samples),
AttachmentLoadOp.Load,
AttachmentStoreOp.Store,
AttachmentLoadOp.Load,
AttachmentStoreOp.Store,
ImageLayout.General,
ImageLayout.General);
var subpassDependency = PipelineConverter.CreateSubpassDependency2();
fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
{
var renderPassCreateInfo = new RenderPassCreateInfo2()
{
SType = StructureType.RenderPassCreateInfo2,
PAttachments = pAttachmentDescs,
AttachmentCount = (uint)attachmentDescs.Length,
PSubpasses = &subpass,
SubpassCount = 1,
PDependencies = &subpassDependency,
DependencyCount = 1
};
gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
using var rp = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
ImageView* attachments = stackalloc ImageView[2];
var srcView = src.GetImageViewForAttachment();
var dstView = dst.GetImageViewForAttachment();
attachments[0] = srcView.Get(cbs).Value;
attachments[1] = dstView.Get(cbs).Value;
var framebufferCreateInfo = new FramebufferCreateInfo()
{
SType = StructureType.FramebufferCreateInfo,
RenderPass = rp.Get(cbs).Value,
AttachmentCount = 2,
PAttachments = attachments,
Width = (uint)src.Width,
Height = (uint)src.Height,
Layers = (uint)src.Layers
};
gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, new[] { srcView, dstView });
var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height));
var clearValue = new ClearValue();
var renderPassBeginInfo = new RenderPassBeginInfo()
{
SType = StructureType.RenderPassBeginInfo,
RenderPass = rp.Get(cbs).Value,
Framebuffer = fb.Get(cbs).Value,
RenderArea = renderArea,
PClearValues = &clearValue,
ClearValueCount = 1
};
// The resolve operation happens at the end of the subpass, so let's just do a begin/end
// to resolve the depth-stencil texture.
// TODO: Do speculative resolve and part of the same render pass as the draw to avoid
// ending the current render pass?
gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
gd.Api.CmdEndRenderPass(cbs.CommandBuffer);
}
}
}
}

View File

@@ -169,12 +169,15 @@ namespace Ryujinx.Graphics.Vulkan
var srcImage = src.GetImage().Get(cbs).Value;
var dstImage = dst.GetImage().Get(cbs).Value;
if (src.Info.Target.IsMultisample())
if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
{
int depth = Math.Min(src.Info.Depth, dst.Info.Depth - firstLayer);
int levels = Math.Min(src.Info.Levels, dst.Info.Levels - firstLevel);
CopyMSToNonMS(_gd, cbs, src, dst, srcImage, dstImage, 0, firstLayer, 0, firstLevel, depth, levels);
int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers);
}
else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample())
{
int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers);
}
else
{
@@ -213,9 +216,13 @@ namespace Ryujinx.Graphics.Vulkan
var srcImage = src.GetImage().Get(cbs).Value;
var dstImage = dst.GetImage().Get(cbs).Value;
if (src.Info.Target.IsMultisample())
if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
{
CopyMSToNonMS(_gd, cbs, src, dst, srcImage, dstImage, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
_gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
}
else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample())
{
_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
}
else
{
@@ -239,142 +246,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private static void CopyMSToNonMS(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
TextureView dst,
Image srcImage,
Image dstImage,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel,
int layers,
int levels)
{
bool differentFormats = src.Info.Format != dst.Info.Format;
var target = src.Info.Target switch
{
Target.Texture2D => Target.Texture2DMultisample,
Target.Texture2DArray => Target.Texture2DMultisampleArray,
Target.Texture2DMultisampleArray => Target.Texture2DArray,
_ => Target.Texture2D
};
var intermmediateTarget = differentFormats ? dst.Info.Target : target;
using var intermmediate = CreateIntermmediateTexture(gd, src, ref dst._info, intermmediateTarget, layers, levels);
var intermmediateImage = intermmediate.GetImage().Get(cbs).Value;
if (differentFormats)
{
// If the formats are different, the resolve would perform format conversion.
// So we need yet another intermmediate texture and do a copy to reinterpret the
// data into the correct (destination) format, without doing any sort of conversion.
using var intermmediate2 = CreateIntermmediateTexture(gd, src, ref src._info, target, layers, levels);
var intermmediate2Image = intermmediate2.GetImage().Get(cbs).Value;
TextureCopy.Copy(
gd.Api,
cbs.CommandBuffer,
srcImage,
intermmediate2Image,
src.Info,
intermmediate2.Info,
src.FirstLayer,
0,
src.FirstLevel,
0,
srcLayer,
0,
srcLevel,
0,
layers,
levels);
TextureCopy.Copy(
gd.Api,
cbs.CommandBuffer,
intermmediate2Image,
intermmediateImage,
intermmediate2.Info,
intermmediate.Info,
0,
0,
0,
0,
0,
0,
0,
0,
layers,
levels);
}
else
{
TextureCopy.Copy(
gd.Api,
cbs.CommandBuffer,
srcImage,
intermmediateImage,
src.Info,
intermmediate.Info,
src.FirstLayer,
0,
src.FirstLevel,
0,
srcLayer,
0,
srcLevel,
0,
layers,
levels);
}
var srcRegion = new Extents2D(0, 0, src.Width, src.Height);
var dstRegion = new Extents2D(0, 0, dst.Width, dst.Height);
TextureCopy.Blit(
gd.Api,
cbs.CommandBuffer,
intermmediateImage,
dstImage,
intermmediate.Info,
dst.Info,
srcRegion,
dstRegion,
0,
dst.FirstLevel + dstLevel,
0,
dst.FirstLayer + dstLayer,
layers,
levels,
true,
ImageAspectFlags.ImageAspectColorBit,
ImageAspectFlags.ImageAspectColorBit);
}
private static TextureView CreateIntermmediateTexture(VulkanRenderer gd, TextureView src, ref TextureCreateInfo formatInfo, Target target, int depth, int levels)
{
return gd.CreateTextureView(new GAL.TextureCreateInfo(
src.Width,
src.Height,
depth,
levels,
1,
formatInfo.BlockWidth,
formatInfo.BlockHeight,
formatInfo.BytesPerPixel,
formatInfo.Format,
DepthStencilMode.Depth,
target,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha), 1f);
}
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
{
var dst = (TextureView)destination;
@@ -421,6 +292,14 @@ namespace Ryujinx.Graphics.Vulkan
src.Width == dst.Width &&
src.Height == dst.Height &&
src.VkFormat == dst.VkFormat)
{
if (src.Info.Samples > 1 && src.Info.Samples != dst.Info.Samples && src.Info.Format.IsDepthOrStencil())
{
// CmdResolveImage does not support depth-stencil resolve, so we need to use an alternative path
// for those textures.
TextureCopy.ResolveDepthStencil(_gd, _device, cbs, src, dst);
}
else
{
TextureCopy.Copy(
_gd.Api,
@@ -439,6 +318,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
layers,
levels);
}
return;
}

View File

@@ -1,4 +1,4 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan
"VUID-VkSubpassDependency-srcSubpass-00867"
};
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugReport debugReport, out DebugReportCallbackEXT debugReportCallback)
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger)
{
var enabledLayers = new List<string>();
@@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Vulkan
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
}
var enabledExtensions = requiredExtensions.Append(ExtDebugReport.ExtensionName).ToArray();
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
var appName = Marshal.StringToHGlobalAnsi(AppName);
@@ -139,22 +139,18 @@ namespace Ryujinx.Graphics.Vulkan
Marshal.FreeHGlobal(ppEnabledLayers[i]);
}
CreateDebugCallbacks(api, logLevel, instance, out debugReport, out debugReportCallback);
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger);
return instance;
}
private unsafe static uint DebugReport(
uint flags,
DebugReportObjectTypeEXT objectType,
ulong @object,
nuint location,
int messageCode,
byte* layerPrefix,
byte* message,
void* userData)
private unsafe static uint DebugMessenger(
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
DebugUtilsMessageTypeFlagsEXT messageTypes,
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
var msg = Marshal.PtrToStringAnsi((IntPtr)message);
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
foreach (string excludedMessagePart in _excludedMessages)
{
@@ -164,26 +160,20 @@ namespace Ryujinx.Graphics.Vulkan
}
}
DebugReportFlagsEXT debugFlags = (DebugReportFlagsEXT)flags;
if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportErrorBitExt))
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt))
{
Logger.Error?.Print(LogClass.Gpu, msg);
//throw new Exception(msg);
}
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportWarningBitExt))
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt))
{
Logger.Warning?.Print(LogClass.Gpu, msg);
}
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportInformationBitExt))
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt))
{
Logger.Info?.Print(LogClass.Gpu, msg);
}
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt))
{
Logger.Warning?.Print(LogClass.Gpu, msg);
}
else
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt))
{
Logger.Debug?.Print(LogClass.Gpu, msg);
}
@@ -403,6 +393,7 @@ namespace Ryujinx.Graphics.Vulkan
ShaderClipDistance = true,
ShaderFloat64 = supportedFeatures.ShaderFloat64,
ShaderImageGatherExtended = true,
ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample,
// ShaderStorageImageReadWithoutFormat = true,
// ShaderStorageImageWriteWithoutFormat = true,
TessellationShader = true,
@@ -551,46 +542,59 @@ namespace Ryujinx.Graphics.Vulkan
return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex);
}
internal unsafe static void CreateDebugCallbacks(
internal unsafe static void CreateDebugMessenger(
Vk api,
GraphicsDebugLevel logLevel,
Instance instance,
out ExtDebugReport debugReport,
out DebugReportCallbackEXT debugReportCallback)
out ExtDebugUtils debugUtils,
out DebugUtilsMessengerEXT debugUtilsMessenger)
{
debugReport = default;
debugUtils = default;
if (logLevel != GraphicsDebugLevel.None)
{
if (!api.TryGetInstanceExtension(instance, out debugReport))
if (!api.TryGetInstanceExtension(instance, out debugUtils))
{
debugReportCallback = default;
debugUtilsMessenger = default;
return;
}
var flags = logLevel switch
var filterLogType = logLevel switch
{
GraphicsDebugLevel.Error => DebugReportFlagsEXT.DebugReportErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugReportFlagsEXT.DebugReportErrorBitExt | DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt,
GraphicsDebugLevel.All => DebugReportFlagsEXT.DebugReportInformationBitExt |
DebugReportFlagsEXT.DebugReportWarningBitExt |
DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt |
DebugReportFlagsEXT.DebugReportErrorBitExt |
DebugReportFlagsEXT.DebugReportDebugBitExt,
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt |
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
};
var debugReportCallbackCreateInfo = new DebugReportCallbackCreateInfoEXT()
var filterLogSeverity = logLevel switch
{
SType = StructureType.DebugReportCallbackCreateInfoExt,
Flags = flags,
PfnCallback = new PfnDebugReportCallbackEXT(DebugReport)
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt |
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt |
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt |
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt |
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
};
debugReport.CreateDebugReportCallback(instance, in debugReportCallbackCreateInfo, null, out debugReportCallback).ThrowOnError();
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
{
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
MessageType = filterLogType,
MessageSeverity = filterLogSeverity,
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger)
};
debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError();
}
else
{
debugReportCallback = default;
debugUtilsMessenger = default;
}
}
}

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
internal ExtDebugReport DebugReportApi { get; private set; }
internal ExtDebugUtils DebugUtilsApi { get; private set; }
internal uint QueueFamilyIndex { get; private set; }
internal Queue Queue { get; private set; }
@@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan
private SyncManager _syncManager;
private PipelineFull _pipeline;
private DebugReportCallbackEXT _debugReportCallback;
private DebugUtilsMessengerEXT _debugUtilsMessenger;
internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline;
@@ -237,9 +237,9 @@ namespace Ryujinx.Graphics.Vulkan
Api = api;
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugReport debugReport, out _debugReportCallback);
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger);
DebugReportApi = debugReport;
DebugUtilsApi = debugUtils;
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
{
@@ -287,9 +287,9 @@ namespace Ryujinx.Graphics.Vulkan
}
}
internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources)
internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null)
{
return new ShaderCollection(this, _device, sources, isMinimal: true);
return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true);
}
public ISampler CreateSampler(GAL.SamplerCreateInfo info)
@@ -584,9 +584,9 @@ namespace Ryujinx.Graphics.Vulkan
MemoryAllocator.Dispose();
if (_debugReportCallback.Handle != 0)
if (_debugUtilsMessenger.Handle != 0)
{
DebugReportApi.DestroyDebugReportCallback(_instance, _debugReportCallback, null);
DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null);
}
foreach (var shader in Shaders)

View File

@@ -32,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private bool _handlesRequestToDisplay = false;
private bool _autoSleepDisabled = false;
private bool _albumImageTakenNotificationEnabled = false;
private bool _recordVolumeMuted = false;
private uint _screenShotImageOrientation = 0;
private uint _idleTimeDetectionExtension = 0;
@@ -389,5 +390,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandHipc(130)] // 13.0.0+
// SetRecordVolumeMuted(b8)
public ResultCode SetRecordVolumeMuted(ServiceCtx context)
{
bool recordVolumeMuted = context.RequestData.ReadBoolean();
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { recordVolumeMuted });
_recordVolumeMuted = recordVolumeMuted;
return ResultCode.Success;
}
}
}

View File

@@ -203,6 +203,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Irs
return ResultCode.Success;
}
[CommandHipc(318)] // 4.0.0+
// StopImageProcessorAsync(nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, pid)
public ResultCode StopImageProcessorAsync(ServiceCtx context)
{
int irCameraHandle = context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle });
return ResultCode.Success;
}
[CommandHipc(319)] // 4.0.0+
// ActivateIrsensorWithFunctionLevel(nn::applet::AppletResourceUserId, nn::irsensor::PackedFunctionLevel, pid)
public ResultCode ActivateIrsensorWithFunctionLevel(ServiceCtx context)

View File

@@ -5,22 +5,48 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
class INfc : IpcService
{
private NfcPermissionLevel _permissionLevel;
private State _state;
public INfc(NfcPermissionLevel permissionLevel)
{
_permissionLevel = permissionLevel;
_state = State.NonInitialized;
}
[CommandHipc(0)]
[CommandHipc(400)] // 4.0.0+
// Initialize()
// Initialize(u64, u64, pid, buffer<unknown, 5>)
public ResultCode Initialize(ServiceCtx context)
{
_state = State.Initialized;
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
return ResultCode.Success;
}
[CommandHipc(1)]
[CommandHipc(401)] // 4.0.0+
// Finalize()
public ResultCode Finalize(ServiceCtx context)
{
_state = State.NonInitialized;
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
return ResultCode.Success;
}
[CommandHipc(2)]
[CommandHipc(402)] // 4.0.0+
// GetState() -> u32
public ResultCode GetState(ServiceCtx context)
{
context.ResponseData.Write((int)_state);
return ResultCode.Success;
}
[CommandHipc(3)]
[CommandHipc(403)] // 4.0.0+
// IsNfcEnabled() -> b8

View File

@@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
{
enum State
{
NonInitialized,
Initialized
}
}

View File

@@ -349,7 +349,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
// GetSessionCacheMode() -> nn::ssl::sf::SessionCacheMode
public ResultCode GetSessionCacheMode(ServiceCtx context)
{
throw new ServiceNotImplementedException(this, context);
context.ResponseData.Write((uint)_sessionCacheMode);
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { _sessionCacheMode });
return ResultCode.Success;
}
[CommandHipc(19)]

View File

@@ -26,7 +26,7 @@
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.0" />
</ItemGroup>
<!-- Due to Concentus. -->

View File

@@ -92,5 +92,31 @@ namespace Ryujinx.Memory.Tests
}
}
}
[Test]
public void Test_AliasMapLeak()
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
ulong pageSize = 4096;
ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that.
using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable);
using MemoryBlock toAlias = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
for (ulong offset = 0; offset < size; offset += pageSize)
{
toAlias.MapView(backing, 0, offset, pageSize);
toAlias.Write(offset, 0xbadc0de);
Assert.AreEqual(0xbadc0de, backing.Read<int>(0));
toAlias.UnmapView(backing, offset, pageSize);
}
}
}
}

View File

@@ -177,7 +177,7 @@ namespace Ryujinx.Memory
public static void UnmapView(IntPtr location, ulong size)
{
mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED, -1, 0);
mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0);
}
}
}

View File

@@ -0,0 +1,199 @@
using System.Runtime.CompilerServices;
namespace Ryujinx.Memory.Tracking
{
/// <summary>
/// A bitmap that can check or set large ranges of true/false values at once.
/// </summary>
struct BitMap
{
public const int IntSize = 64;
private const int IntShift = 6;
private const int IntMask = IntSize - 1;
/// <summary>
/// Masks representing the bitmap. Least significant bit first, 64-bits per mask.
/// </summary>
public readonly long[] Masks;
/// <summary>
/// Create a new bitmap.
/// </summary>
/// <param name="count">The number of bits to reserve</param>
public BitMap(int count)
{
Masks = new long[(count + IntMask) / IntSize];
}
/// <summary>
/// Check if any bit in the bitmap is set.
/// </summary>
/// <returns>True if any bits are set, false otherwise</returns>
public bool AnySet()
{
for (int i = 0; i < Masks.Length; i++)
{
if (Masks[i] != 0)
{
return true;
}
}
return false;
}
/// <summary>
/// Check if a bit in the bitmap is set.
/// </summary>
/// <param name="bit">The bit index to check</param>
/// <returns>True if the bit is set, false otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSet(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
return (Masks[wordIndex] & wordMask) != 0;
}
/// <summary>
/// Check if any bit in a range of bits in the bitmap are set. (inclusive)
/// </summary>
/// <param name="start">The first bit index to check</param>
/// <param name="end">The last bit index to check</param>
/// <returns>True if a bit is set, false otherwise</returns>
public bool IsSet(int start, int end)
{
if (start == end)
{
return IsSet(start);
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
return (Masks[startIndex] & startMask & endMask) != 0;
}
if ((Masks[startIndex] & startMask) != 0)
{
return true;
}
for (int i = startIndex + 1; i < endIndex; i++)
{
if (Masks[i] != 0)
{
return true;
}
}
if ((Masks[endIndex] & endMask) != 0)
{
return true;
}
return false;
}
/// <summary>
/// Set a bit at a specific index to 1.
/// </summary>
/// <param name="bit">The bit index to set</param>
/// <returns>True if the bit is set, false if it was already set</returns>
public bool Set(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
if ((Masks[wordIndex] & wordMask) != 0)
{
return false;
}
Masks[wordIndex] |= wordMask;
return true;
}
/// <summary>
/// Set a range of bits in the bitmap to 1.
/// </summary>
/// <param name="start">The first bit index to set</param>
/// <param name="end">The last bit index to set</param>
public void SetRange(int start, int end)
{
if (start == end)
{
Set(start);
return;
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
Masks[startIndex] |= startMask & endMask;
}
else
{
Masks[startIndex] |= startMask;
for (int i = startIndex + 1; i < endIndex; i++)
{
Masks[i] |= -1;
}
Masks[endIndex] |= endMask;
}
}
/// <summary>
/// Clear a bit at a specific index to 0.
/// </summary>
/// <param name="bit">The bit index to clear</param>
/// <returns>True if the bit was set, false if it was not</returns>
public bool Clear(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
bool wasSet = (Masks[wordIndex] & wordMask) != 0;
Masks[wordIndex] &= ~wordMask;
return wasSet;
}
/// <summary>
/// Clear the bitmap entirely, setting all bits to 0.
/// </summary>
public void Clear()
{
for (int i = 0; i < Masks.Length; i++)
{
Masks[i] = 0;
}
}
}
}

View File

@@ -0,0 +1,161 @@
using System;
using System.Threading;
namespace Ryujinx.Memory.Tracking
{
/// <summary>
/// A bitmap that can be safely modified from multiple threads.
/// </summary>
internal class ConcurrentBitmap
{
public const int IntSize = 64;
public const int IntShift = 6;
public const int IntMask = IntSize - 1;
/// <summary>
/// Masks representing the bitmap. Least significant bit first, 64-bits per mask.
/// </summary>
public readonly long[] Masks;
/// <summary>
/// Create a new multithreaded bitmap.
/// </summary>
/// <param name="count">The number of bits to reserve</param>
/// <param name="set">Whether the bits should be initially set or not</param>
public ConcurrentBitmap(int count, bool set)
{
Masks = new long[(count + IntMask) / IntSize];
if (set)
{
Array.Fill(Masks, -1L);
}
}
/// <summary>
/// Check if any bit in the bitmap is set.
/// </summary>
/// <returns>True if any bits are set, false otherwise</returns>
public bool AnySet()
{
for (int i = 0; i < Masks.Length; i++)
{
if (Volatile.Read(ref Masks[i]) != 0)
{
return true;
}
}
return false;
}
/// <summary>
/// Check if a bit in the bitmap is set.
/// </summary>
/// <param name="bit">The bit index to check</param>
/// <returns>True if the bit is set, false otherwise</returns>
public bool IsSet(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
return (Volatile.Read(ref Masks[wordIndex]) & wordMask) != 0;
}
/// <summary>
/// Check if any bit in a range of bits in the bitmap are set. (inclusive)
/// </summary>
/// <param name="start">The first bit index to check</param>
/// <param name="end">The last bit index to check</param>
/// <returns>True if a bit is set, false otherwise</returns>
public bool IsSet(int start, int end)
{
if (start == end)
{
return IsSet(start);
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
long startValue = Volatile.Read(ref Masks[startIndex]);
if (startIndex == endIndex)
{
return (startValue & startMask & endMask) != 0;
}
if ((startValue & startMask) != 0)
{
return true;
}
for (int i = startIndex + 1; i < endIndex; i++)
{
if (Volatile.Read(ref Masks[i]) != 0)
{
return true;
}
}
long endValue = Volatile.Read(ref Masks[endIndex]);
if ((endValue & endMask) != 0)
{
return true;
}
return false;
}
/// <summary>
/// Set a bit at a specific index to either true or false.
/// </summary>
/// <param name="bit">The bit index to set</param>
/// <param name="value">Whether the bit should be set or not</param>
public void Set(int bit, bool value)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
long existing;
long newValue;
do
{
existing = Volatile.Read(ref Masks[wordIndex]);
if (value)
{
newValue = existing | wordMask;
}
else
{
newValue = existing & ~wordMask;
}
}
while (Interlocked.CompareExchange(ref Masks[wordIndex], newValue, existing) != existing);
}
/// <summary>
/// Clear the bitmap entirely, setting all bits to 0.
/// </summary>
public void Clear()
{
for (int i = 0; i < Masks.Length; i++)
{
Volatile.Write(ref Masks[i], 0);
}
}
}
}

View File

@@ -176,6 +176,26 @@ namespace Ryujinx.Memory.Tracking
}
}
/// <summary>
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
/// </summary>
/// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param>
/// <param name="bitmap">The bitmap owning the dirty flag for this handle</param>
/// <param name="bit">The bit of this handle within the dirty flag</param>
/// <returns>The memory tracking handle</returns>
internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit)
{
(address, size) = PageAlign(address, size);
lock (TrackingLock)
{
RegionHandle handle = new RegionHandle(this, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size));
return handle;
}
}
/// <summary>
/// Signal that a virtual memory event happened at the given location (one byte).
/// </summary>

View File

@@ -1,11 +1,15 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Ryujinx.Memory.Tracking
{
/// <summary>
/// A region handle that tracks a large region using many smaller handles, to provide
/// granular tracking that can be used to track partial updates.
/// granular tracking that can be used to track partial updates. Backed by a bitmap
/// to improve performance when scanning large regions.
/// </summary>
public class MultiRegionHandle : IMultiRegionHandle
{
@@ -17,6 +21,12 @@ namespace Ryujinx.Memory.Tracking
private readonly ulong Granularity;
private readonly ulong Size;
private ConcurrentBitmap _dirtyBitmap;
private int _sequenceNumber;
private BitMap _sequenceNumberBitmap;
private int _uncheckedHandles;
public bool Dirty { get; private set; } = true;
internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
@@ -24,6 +34,9 @@ namespace Ryujinx.Memory.Tracking
_handles = new RegionHandle[size / granularity];
Granularity = granularity;
_dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
_sequenceNumberBitmap = new BitMap(_handles.Length);
int i = 0;
if (handles != null)
@@ -40,15 +53,20 @@ namespace Ryujinx.Memory.Tracking
// Fill any gap left before this handle.
while (i < startIndex)
{
RegionHandle fillHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
fillHandle.Parent = this;
_handles[i++] = fillHandle;
}
if (handle.Size == granularity)
lock (tracking.TrackingLock)
{
if (handle is RegionHandle bitHandle && handle.Size == granularity)
{
handle.Parent = this;
_handles[i++] = handle;
bitHandle.ReplaceBitmap(_dirtyBitmap, i);
_handles[i++] = bitHandle;
}
else
{
@@ -56,7 +74,7 @@ namespace Ryujinx.Memory.Tracking
while (i < endIndex)
{
RegionHandle splitHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
splitHandle.Parent = this;
splitHandle.Reprotect(handle.Dirty);
@@ -74,19 +92,32 @@ namespace Ryujinx.Memory.Tracking
}
}
}
}
// Fill any remaining space with new handles.
while (i < _handles.Length)
{
RegionHandle handle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
handle.Parent = this;
_handles[i++] = handle;
}
_uncheckedHandles = _handles.Length;
Address = address;
Size = size;
}
public void SignalWrite()
{
Dirty = true;
}
public IEnumerable<RegionHandle> GetHandles()
{
return _handles;
}
public void ForceDirty(ulong address, ulong size)
{
Dirty = true;
@@ -96,21 +127,15 @@ namespace Ryujinx.Memory.Tracking
for (int i = startHandle; i <= lastHandle; i++)
{
_handles[i].SequenceNumber--;
if (_sequenceNumberBitmap.Clear(i))
{
_uncheckedHandles++;
}
_handles[i].ForceDirty();
}
}
public IEnumerable<RegionHandle> GetHandles()
{
return _handles;
}
public void SignalWrite()
{
Dirty = true;
}
public void QueryModified(Action<ulong, ulong> modifiedAction)
{
if (!Dirty)
@@ -123,33 +148,95 @@ namespace Ryujinx.Memory.Tracking
QueryModified(Address, Size, modifiedAction);
}
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ParseDirtyBits(long dirtyBits, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
{
int startHandle = (int)((address - Address) / Granularity);
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
ulong rgStart = _handles[startHandle].Address;
ulong rgSize = 0;
for (int i = startHandle; i <= lastHandle; i++)
while (dirtyBits != 0)
{
RegionHandle handle = _handles[i];
int bit = BitOperations.TrailingZeroCount(dirtyBits);
dirtyBits &= ~(1L << bit);
int handleIndex = baseBit + bit;
RegionHandle handle = _handles[handleIndex];
if (handleIndex != prevHandle + 1)
{
// Submit handles scanned until the gap as dirty
if (rgSize != 0)
{
modifiedAction(rgStart, rgSize);
rgSize = 0;
}
rgStart = handle.Address;
}
if (handle.Dirty)
{
rgSize += handle.Size;
handle.Reprotect();
}
prevHandle = handleIndex;
}
baseBit += ConcurrentBitmap.IntSize;
}
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
{
int startHandle = (int)((address - Address) / Granularity);
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
ulong rgStart = _handles[startHandle].Address;
if (startHandle == lastHandle)
{
RegionHandle handle = _handles[startHandle];
if (handle.Dirty)
{
handle.Reprotect();
modifiedAction(rgStart, handle.Size);
}
return;
}
ulong rgSize = 0;
long[] masks = _dirtyBitmap.Masks;
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
int startBit = startHandle & ConcurrentBitmap.IntMask;
long startMask = -1L << startBit;
int endIndex = lastHandle >> ConcurrentBitmap.IntShift;
int endBit = lastHandle & ConcurrentBitmap.IntMask;
long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit));
long startValue = Volatile.Read(ref masks[startIndex]);
int baseBit = startIndex << ConcurrentBitmap.IntShift;
int prevHandle = startHandle - 1;
if (startIndex == endIndex)
{
ParseDirtyBits(startValue & startMask & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
}
else
{
// Submit the region scanned so far as dirty
if (rgSize != 0)
ParseDirtyBits(startValue & startMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
for (int i = startIndex + 1; i < endIndex; i++)
{
modifiedAction(rgStart, rgSize);
rgSize = 0;
}
rgStart = handle.EndAddress;
ParseDirtyBits(Volatile.Read(ref masks[i]), ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
}
long endValue = Volatile.Read(ref masks[endIndex]);
ParseDirtyBits(endValue & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
}
if (rgSize != 0)
@@ -158,35 +245,120 @@ namespace Ryujinx.Memory.Tracking
}
}
[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)
{
long seqMask = mask & ~seqMasks[index];
dirtyBits &= seqMask;
while (dirtyBits != 0)
{
int bit = BitOperations.TrailingZeroCount(dirtyBits);
dirtyBits &= ~(1L << bit);
int handleIndex = baseBit + bit;
RegionHandle handle = _handles[handleIndex];
if (handleIndex != prevHandle + 1)
{
// Submit handles scanned until the gap as dirty
if (rgSize != 0)
{
modifiedAction(rgStart, rgSize);
rgSize = 0;
}
rgStart = handle.Address;
}
rgSize += handle.Size;
handle.Reprotect();
prevHandle = handleIndex;
}
seqMasks[index] |= mask;
_uncheckedHandles -= BitOperations.PopCount((ulong)seqMask);
baseBit += ConcurrentBitmap.IntSize;
}
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber)
{
int startHandle = (int)((address - Address) / Granularity);
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
ulong rgStart = _handles[startHandle].Address;
ulong rgStart = Address + (ulong)startHandle * Granularity;
if (sequenceNumber != _sequenceNumber)
{
if (_uncheckedHandles != _handles.Length)
{
_sequenceNumberBitmap.Clear();
_uncheckedHandles = _handles.Length;
}
_sequenceNumber = sequenceNumber;
}
if (startHandle == lastHandle)
{
var handle = _handles[startHandle];
if (_sequenceNumberBitmap.Set(startHandle))
{
_uncheckedHandles--;
if (handle.DirtyOrVolatile())
{
handle.Reprotect();
modifiedAction(rgStart, handle.Size);
}
}
return;
}
if (_uncheckedHandles == 0)
{
return;
}
ulong rgSize = 0;
for (int i = startHandle; i <= lastHandle; i++)
{
RegionHandle handle = _handles[i];
long[] seqMasks = _sequenceNumberBitmap.Masks;
long[] masks = _dirtyBitmap.Masks;
if (sequenceNumber != handle.SequenceNumber && handle.DirtyOrVolatile())
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
int startBit = startHandle & ConcurrentBitmap.IntMask;
long startMask = -1L << startBit;
int endIndex = lastHandle >> ConcurrentBitmap.IntShift;
int endBit = lastHandle & ConcurrentBitmap.IntMask;
long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit));
long startValue = Volatile.Read(ref masks[startIndex]);
int baseBit = startIndex << ConcurrentBitmap.IntShift;
int prevHandle = startHandle - 1;
if (startIndex == endIndex)
{
rgSize += handle.Size;
handle.Reprotect();
ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
}
else
{
// Submit the region scanned so far as dirty
if (rgSize != 0)
ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
for (int i = startIndex + 1; i < endIndex; i++)
{
modifiedAction(rgStart, rgSize);
rgSize = 0;
}
rgStart = handle.EndAddress;
ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
}
handle.SequenceNumber = sequenceNumber;
long endValue = Volatile.Read(ref masks[endIndex]);
ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
}
if (rgSize != 0)

View File

@@ -1,5 +1,4 @@
using Ryujinx.Memory.Range;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -10,7 +9,7 @@ namespace Ryujinx.Memory.Tracking
/// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made,
/// and an action can be performed when the region is read to or written from.
/// </summary>
public class RegionHandle : IRegionHandle, IRange
public class RegionHandle : IRegionHandle
{
/// <summary>
/// If more than this number of checks have been performed on a dirty flag since its last reprotect,
@@ -23,7 +22,20 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
private static int VolatileThreshold = 5;
public bool Dirty { get; private set; }
public bool Dirty
{
get
{
return Bitmap.IsSet(DirtyBit);
}
protected set
{
Bitmap.Set(DirtyBit, value);
}
}
internal int SequenceNumber { get; set; }
public bool Unmapped { get; private set; }
public ulong Address { get; }
@@ -31,7 +43,6 @@ namespace Ryujinx.Memory.Tracking
public ulong EndAddress { get; }
internal IMultiRegionHandle Parent { get; set; }
internal int SequenceNumber { get; set; }
private event Action _onDirty;
@@ -68,17 +79,26 @@ namespace Ryujinx.Memory.Tracking
internal RegionSignal PreAction => _preAction;
internal ConcurrentBitmap Bitmap;
internal int DirtyBit;
/// <summary>
/// Create a new region handle. The handle is registered with the given tracking object,
/// Create a new bitmap backed region handle. The handle is registered with the given tracking object,
/// and will be notified of any changes to the specified region.
/// </summary>
/// <param name="tracking">Tracking object for the target memory block</param>
/// <param name="address">Virtual address of the region to track</param>
/// <param name="size">Size of the region to track</param>
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
/// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true)
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ConcurrentBitmap bitmap, int bit, bool mapped = true)
{
Bitmap = bitmap;
DirtyBit = bit;
Dirty = mapped;
Unmapped = !mapped;
Address = address;
Size = size;
@@ -92,6 +112,54 @@ namespace Ryujinx.Memory.Tracking
}
}
/// <summary>
/// Create a new region handle. The handle is registered with the given tracking object,
/// and will be notified of any changes to the specified region.
/// </summary>
/// <param name="tracking">Tracking object for the target memory block</param>
/// <param name="address">Virtual address of the region to track</param>
/// <param name="size">Size of the region to track</param>
/// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true)
{
Bitmap = new ConcurrentBitmap(1, mapped);
Unmapped = !mapped;
Address = address;
Size = size;
EndAddress = address + size;
_tracking = tracking;
_regions = tracking.GetVirtualRegionsForHandle(address, size);
foreach (var region in _regions)
{
region.Handles.Add(this);
}
}
/// <summary>
/// Replace the bitmap and bit index used to track dirty state.
/// </summary>
/// <remarks>
/// The tracking lock should be held when this is called, to ensure neither bitmap is modified.
/// </remarks>
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
internal void ReplaceBitmap(ConcurrentBitmap bitmap, int bit)
{
// Assumes the tracking lock is held, so nothing else can signal right now.
var oldBitmap = Bitmap;
var oldBit = DirtyBit;
bitmap.Set(bit, Dirty);
Bitmap = bitmap;
DirtyBit = bit;
Dirty |= oldBitmap.IsSet(oldBit);
}
/// <summary>
/// Clear the volatile state of this handle.
/// </summary>
@@ -108,7 +176,7 @@ namespace Ryujinx.Memory.Tracking
public bool DirtyOrVolatile()
{
_checkCount++;
return Dirty || _volatile;
return _volatile || Dirty;
}
/// <summary>
@@ -195,6 +263,8 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
public void ForceDirty()
{
_checkCount++;
Dirty = true;
}

View File

@@ -3,9 +3,10 @@ using System;
namespace Ryujinx.Tests.Unicorn
{
public class UnicornAArch32
public class UnicornAArch32 : IDisposable
{
internal readonly IntPtr uc;
private bool _isDisposed = false;
public IndexedProperty<int, uint> R
{
@@ -106,8 +107,23 @@ namespace Ryujinx.Tests.Unicorn
}
~UnicornAArch32()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
Interface.Checked(Native.Interface.uc_close(uc));
_isDisposed = true;
}
}
public void RunForCount(ulong count)

View File

@@ -3,9 +3,10 @@ using System;
namespace Ryujinx.Tests.Unicorn
{
public class UnicornAArch64
public class UnicornAArch64 : IDisposable
{
internal readonly IntPtr uc;
private bool _isDisposed = false;
public IndexedProperty<int, ulong> X
{
@@ -95,8 +96,23 @@ namespace Ryujinx.Tests.Unicorn
}
~UnicornAArch64()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
Interface.Checked(Native.Interface.uc_close(uc));
_isDisposed = true;
}
}
public void RunForCount(ulong count)

View File

@@ -80,6 +80,12 @@ namespace Ryujinx.Tests.Cpu
[TearDown]
public void Teardown()
{
if (_unicornAvailable)
{
_unicornEmu.Dispose();
_unicornEmu = null;
}
_memory.DecrementReferenceCount();
_context.Dispose();
_ram.Dispose();

View File

@@ -76,6 +76,12 @@ namespace Ryujinx.Tests.Cpu
[TearDown]
public void Teardown()
{
if (_unicornAvailable)
{
_unicornEmu.Dispose();
_unicornEmu = null;
}
_memory.DecrementReferenceCount();
_context.Dispose();
_ram.Dispose();

View File

@@ -339,6 +339,93 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Explicit]
[Test, Pairwise, Description("VCVT<top>.F16.F32 <Sd>, <Dm>")]
public void Vcvt_F32_F16([Values(0u, 1u, 2u, 3u)] uint rd,
[Values(0u, 1u, 2u, 3u)] uint rm,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3,
[Values] bool top)
{
uint opcode = 0xeeb30a40; // VCVTB.F16.F32 S0, D0
if (top)
{
opcode |= 1 << 7;
}
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5);
V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
[Explicit]
[Test, Pairwise, Description("VCVT<top>.F16.F64 <Sd>, <Dm>")]
public void Vcvt_F64_F16([Values(0u, 1u, 2u, 3u)] uint rd,
[Values(0u, 1u)] uint rm,
[ValueSource(nameof(_1D_F_))] ulong d0,
[ValueSource(nameof(_1D_F_))] ulong d1,
[Values] bool top)
{
uint opcode = 0xeeb30b40; // VCVTB.F16.F64 S0, D0
if (top)
{
opcode |= 1 << 7;
}
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
V128 v0 = MakeVectorE0E1(d0, d1);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
[Explicit]
[Test, Pairwise, Description("VCVT<top>.F<size>.F16 <Vd>, <Sm>")]
public void Vcvt_F16_Fx([Values(0u, 1u, 2u, 3u)] uint rd,
[Values(0u, 1u, 2u, 3u)] uint rm,
[ValueSource(nameof(_1D_F_))] ulong d0,
[ValueSource(nameof(_1D_F_))] ulong d1,
[Values] bool top,
[Values] bool sz)
{
uint opcode = 0xeeb20a40; // VCVTB.F32.F16 S0, S0
if (top)
{
opcode |= 1 << 7;
}
if (sz)
{
opcode |= 1 << 8;
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
}
else
{
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
}
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
V128 v0 = MakeVectorE0E1(d0, d1);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
#endif
}
}

View File

@@ -23,7 +23,7 @@
<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="OpenTK.Graphics" Version="4.7.2" />
<PackageReference Include="SPB" Version="0.0.4-build24" />
<PackageReference Include="SPB" Version="0.0.4-build27" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
</ItemGroup>