Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
b96794e72b | |||
f1d1670b0b | |||
b8de72de8f | |||
eebc39228d | |||
9daf029f35 | |||
51a27032f0 | |||
a6a67a2b7a | |||
c6d05301aa | |||
647de4cd31 | |||
f82309fa2d | |||
7d8e198c33 | |||
3d98e1361b | |||
141cf61ff7 | |||
3fe3598d41 | |||
59cdf310bd | |||
4e34170a84 | |||
d540af5dc0 | |||
f7c7b66fc0 | |||
28ba55598d | |||
9719b6a112 | |||
f70236f947 | |||
eafadf10c7 | |||
9b06ee7736 | |||
baba2c2467 | |||
286e5d39b2 | |||
dc529c1181 | |||
c7cf1cbc35 | |||
d8e487d018 | |||
5fdc46ac7f | |||
1e5b45f580 | |||
62585755fd | |||
56621615b1 | |||
2099a3e84b | |||
7d26e4ac7b | |||
8d41402fa6 | |||
5af8ce7c38 | |||
77c4291c34 | |||
6e92b7a378 | |||
9b852c7481 | |||
c40c3905e2 | |||
a6cd044f0f | |||
f5a1de6ac5 | |||
2aeb5b00e3 | |||
60ba7b71f2 | |||
7c1d2bbb98 | |||
beacf8c1c8 | |||
0dbe45ae37 | |||
2b50e52e48 | |||
49eadbc209 | |||
2df16ded9b |
24
.github/dependabot.yml
vendored
Normal file
24
.github/dependabot.yml
vendored
Normal 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
|
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -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
|
||||
dotnet-version: 7.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
|
||||
|
12
.github/workflows/nightly_pr_comment.yml
vendored
12
.github/workflows/nightly_pr_comment.yml
vendored
@ -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});
|
||||
}
|
||||
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@ -25,10 +25,10 @@ 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
|
||||
dotnet-version: 7.0.x
|
||||
- name: Ensure NuGet Source
|
||||
uses: fabriciomurta/ensure-nuget-source@v1
|
||||
- name: Clear
|
||||
@ -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: |
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
44
ARMeilleure/Decoders/OpCode32SimdCvtTB.cs
Normal file
44
ARMeilleure/Decoders/OpCode32SimdCvtTB.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -1617,18 +1617,32 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Frinta_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
||||
});
|
||||
EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearestAway);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Frinta_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
||||
});
|
||||
EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearestAway);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Frinti_S(ArmEmitterContext context)
|
||||
@ -3516,9 +3530,18 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss;
|
||||
Operand res;
|
||||
|
||||
Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss;
|
||||
|
||||
res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: true);
|
||||
}
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
@ -3538,9 +3561,18 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps;
|
||||
Operand res;
|
||||
|
||||
Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps;
|
||||
|
||||
res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: false);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
|
@ -164,32 +164,74 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void Fcvtas_Gp(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41Fcvts_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcvtas_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true);
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcvtas_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false);
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcvtau_Gp(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41Fcvtu_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcvtau_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true);
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcvtau_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false);
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcvtl_V(ArmEmitterContext context)
|
||||
@ -1223,7 +1265,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||
}
|
||||
|
||||
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
||||
|
||||
@ -1265,7 +1314,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||
}
|
||||
|
||||
Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar);
|
||||
|
||||
@ -1314,7 +1370,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
@ -1369,7 +1432,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
@ -1424,7 +1494,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
|
||||
? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes)
|
||||
@ -1464,7 +1541,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
|
||||
? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes)
|
||||
@ -1512,7 +1596,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
@ -1567,7 +1658,14 @@ namespace ARMeilleure.Instructions
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
|
@ -203,6 +203,9 @@ namespace ARMeilleure.Instructions
|
||||
FPRoundingMode roundMode;
|
||||
switch (rm)
|
||||
{
|
||||
case 0b00:
|
||||
roundMode = FPRoundingMode.ToNearestAway;
|
||||
break;
|
||||
case 0b01:
|
||||
roundMode = FPRoundingMode.ToNearest;
|
||||
break;
|
||||
@ -228,7 +231,7 @@ namespace ARMeilleure.Instructions
|
||||
bool unsigned = op.Opc == 0;
|
||||
int rm = op.Opc2 & 3;
|
||||
|
||||
if (Optimizations.UseSse41 && rm != 0b00)
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
|
||||
}
|
||||
@ -258,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)
|
||||
{
|
||||
@ -267,15 +338,21 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
int rm = op.Opc2 & 3;
|
||||
|
||||
if (Optimizations.UseSse2 && rm != 0b00)
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitScalarUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
||||
|
||||
FPRoundingMode roundMode = RMToRoundMode(rm);
|
||||
|
||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
@ -305,7 +382,17 @@ namespace ARMeilleure.Instructions
|
||||
// VRINTA (vector).
|
||||
public static void Vrinta_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: false);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTM (vector).
|
||||
@ -413,7 +500,14 @@ namespace ARMeilleure.Instructions
|
||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
@ -464,7 +558,14 @@ namespace ARMeilleure.Instructions
|
||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
|
@ -33,6 +33,14 @@ namespace ARMeilleure.Instructions
|
||||
};
|
||||
|
||||
public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0;
|
||||
|
||||
public static ulong X86GetGf2p8LogicalShiftLeft(int shift)
|
||||
{
|
||||
ulong identity = (0b00000001UL << 56) | (0b00000010UL << 48) | (0b00000100UL << 40) | (0b00001000UL << 32) |
|
||||
(0b00010000UL << 24) | (0b00100000UL << 16) | (0b01000000UL << 8) | (0b10000000UL << 0);
|
||||
|
||||
return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "X86 SSE Intrinsics"
|
||||
@ -243,19 +251,44 @@ namespace ARMeilleure.Instructions
|
||||
throw new ArgumentException($"Invalid rounding mode \"{roundMode}\".");
|
||||
}
|
||||
|
||||
public static ulong X86GetGf2p8LogicalShiftLeft(int shift)
|
||||
public static Operand EmitSse41RoundToNearestWithTiesToAwayOpF(ArmEmitterContext context, Operand n, bool scalar)
|
||||
{
|
||||
ulong identity =
|
||||
(0b00000001UL << 56) |
|
||||
(0b00000010UL << 48) |
|
||||
(0b00000100UL << 40) |
|
||||
(0b00001000UL << 32) |
|
||||
(0b00010000UL << 24) |
|
||||
(0b00100000UL << 16) |
|
||||
(0b01000000UL << 8) |
|
||||
(0b10000000UL << 0);
|
||||
Debug.Assert(n.Type == OperandType.V128);
|
||||
|
||||
return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8);
|
||||
Operand nCopy = context.Copy(n);
|
||||
|
||||
Operand rC = Const(X86GetRoundControl(FPRoundingMode.TowardsZero));
|
||||
|
||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||
|
||||
if ((op.Size & 1) == 0)
|
||||
{
|
||||
Operand signMask = scalar ? X86GetScalar(context, int.MinValue) : X86GetAllElements(context, int.MinValue);
|
||||
signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy);
|
||||
|
||||
// 0x3EFFFFFF == BitConverter.SingleToInt32Bits(0.5f) - 1
|
||||
Operand valueMask = scalar ? X86GetScalar(context, 0x3EFFFFFF) : X86GetAllElements(context, 0x3EFFFFFF);
|
||||
valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask);
|
||||
|
||||
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addss : Intrinsic.X86Addps, nCopy, valueMask);
|
||||
|
||||
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundss : Intrinsic.X86Roundps, nCopy, rC);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand signMask = scalar ? X86GetScalar(context, long.MinValue) : X86GetAllElements(context, long.MinValue);
|
||||
signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy);
|
||||
|
||||
// 0x3FDFFFFFFFFFFFFFL == BitConverter.DoubleToInt64Bits(0.5d) - 1L
|
||||
Operand valueMask = scalar ? X86GetScalar(context, 0x3FDFFFFFFFFFFFFFL) : X86GetAllElements(context, 0x3FDFFFFFFFFFFFFFL);
|
||||
valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask);
|
||||
|
||||
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addsd : Intrinsic.X86Addpd, nCopy, valueMask);
|
||||
|
||||
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundsd : Intrinsic.X86Roundpd, nCopy, rC);
|
||||
}
|
||||
|
||||
return nCopy;
|
||||
}
|
||||
|
||||
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
||||
|
@ -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);
|
||||
|
@ -2,9 +2,10 @@ namespace ARMeilleure.State
|
||||
{
|
||||
public enum FPRoundingMode
|
||||
{
|
||||
ToNearest = 0,
|
||||
ToNearest = 0, // With ties to even.
|
||||
TowardsPlusInfinity = 1,
|
||||
TowardsMinusInfinity = 2,
|
||||
TowardsZero = 3
|
||||
TowardsZero = 3,
|
||||
ToNearestAway = 4 // With ties to away.
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 3710; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 3713; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
@ -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];
|
||||
|
@ -36,8 +36,8 @@
|
||||
|
||||
## Compatibility
|
||||
|
||||
As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 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
|
||||
|
||||
@ -62,7 +62,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
|
||||
If you wish to build the emulator yourself, follow these steps:
|
||||
|
||||
### Step 1
|
||||
Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0).
|
||||
Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
|
||||
|
||||
### Step 2
|
||||
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||
@ -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**
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
@ -150,7 +150,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
/// <param name="inputHeader">The splitter header.</param>
|
||||
/// <param name="input">The raw data after the splitter header.</param>
|
||||
private void UpdateState(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
|
||||
private void UpdateState(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
|
||||
{
|
||||
for (int i = 0; i < inputHeader.SplitterCount; i++)
|
||||
{
|
||||
@ -177,7 +177,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
/// <param name="inputHeader">The splitter header.</param>
|
||||
/// <param name="input">The raw data after the splitter header.</param>
|
||||
private void UpdateData(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
|
||||
private void UpdateData(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
|
||||
{
|
||||
for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -10,6 +10,7 @@ using Ryujinx.Ava.Ui.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@ -64,8 +65,7 @@ namespace Ryujinx.Ava
|
||||
if (result == UserResult.Yes)
|
||||
{
|
||||
var path = Process.GetCurrentProcess().MainModule.FileName;
|
||||
var info = new ProcessStartInfo() { FileName = path, UseShellExecute = false };
|
||||
var proc = Process.Start(info);
|
||||
var proc = Process.Start(path, CommandLineState.Arguments);
|
||||
desktop.Shutdown();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
@ -587,10 +587,12 @@
|
||||
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
|
||||
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
|
||||
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
|
||||
"SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied",
|
||||
"SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied",
|
||||
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
|
||||
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
|
||||
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
|
||||
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
|
||||
"VolumeShort": "Vol"
|
||||
"VolumeShort": "Vol",
|
||||
"SettingsEnableMacroHLE": "Enable Macro HLE",
|
||||
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure."
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -588,5 +588,9 @@
|
||||
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
||||
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
|
||||
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
|
||||
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?"
|
||||
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?",
|
||||
"RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?",
|
||||
"SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:",
|
||||
"SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:",
|
||||
"VolumeShort": "Głoś"
|
||||
}
|
||||
|
@ -556,5 +556,7 @@
|
||||
"SettingsSelectThemeFileDialogTitle" : "Selecionar arquivo do tema",
|
||||
"SettingsXamlThemeFile" : "Arquivo de tema Xaml",
|
||||
"SettingsTabHotkeysResScaleUpHotkey": "Aumentar a resolução:",
|
||||
"SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:"
|
||||
"SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:",
|
||||
"SettingsEnableMacroHLE": "Habilitar emulação de alto nível para Macros",
|
||||
"SettingsEnableMacroHLETooltip": "Habilita emulação de alto nível de códigos Macro da GPU.\n\nMelhora a performance, mas pode causar problemas gráficos em alguns jogos.\n\nEm caso de dúvida, deixe ATIVADO."
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray());
|
||||
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
|
@ -13,6 +13,7 @@ using Ryujinx.Common.SystemInfo;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -26,7 +27,6 @@ namespace Ryujinx.Ava
|
||||
public static double ActualScaleFactor { get; set; }
|
||||
public static string Version { get; private set; }
|
||||
public static string ConfigurationPath { get; private set; }
|
||||
public static string CommandLineProfile { get; set; }
|
||||
public static bool PreviewerDetached { get; private set; }
|
||||
|
||||
public static RenderTimer RenderTimer { get; private set; }
|
||||
@ -87,46 +87,8 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static void Initialize(string[] args)
|
||||
{
|
||||
// Parse Arguments.
|
||||
string launchPathArg = null;
|
||||
string baseDirPathArg = null;
|
||||
bool startFullscreenArg = false;
|
||||
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
{
|
||||
string arg = args[i];
|
||||
|
||||
if (arg == "-r" || arg == "--root-data-dir")
|
||||
{
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
baseDirPathArg = args[++i];
|
||||
}
|
||||
else if (arg == "-p" || arg == "--profile")
|
||||
{
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CommandLineProfile = args[++i];
|
||||
}
|
||||
else if (arg == "-f" || arg == "--fullscreen")
|
||||
{
|
||||
startFullscreenArg = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
launchPathArg = arg;
|
||||
}
|
||||
}
|
||||
// Parse arguments
|
||||
CommandLineState.ParseArguments(args);
|
||||
|
||||
// Delete backup files after updating.
|
||||
Task.Run(Updater.CleanupUpdate);
|
||||
@ -135,10 +97,10 @@ namespace Ryujinx.Ava
|
||||
|
||||
// Hook unhandled exception and process exit events.
|
||||
AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
||||
AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit();
|
||||
AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit();
|
||||
|
||||
// Setup base data directory.
|
||||
AppDataManager.Initialize(baseDirPathArg);
|
||||
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
|
||||
|
||||
// Initialize the configuration.
|
||||
ConfigurationState.Initialize();
|
||||
@ -173,9 +135,9 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
}
|
||||
|
||||
if (launchPathArg != null)
|
||||
if (CommandLineState.LaunchPathArg != null)
|
||||
{
|
||||
MainWindow.DeferLoadApplication(launchPathArg, startFullscreenArg);
|
||||
MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,6 +177,19 @@ namespace Ryujinx.Ava
|
||||
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if graphics backend was overridden
|
||||
if (CommandLineState.OverrideGraphicsBackend != null)
|
||||
{
|
||||
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
|
||||
}
|
||||
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintSystemInfo()
|
||||
|
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@ -8,11 +8,13 @@
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<RootNamespace>Ryujinx.Ava</RootNamespace>
|
||||
<ApplicationIcon>Ryujinx.ico</ApplicationIcon>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<TrimMode>partial</TrimMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -34,7 +36,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>
|
||||
|
@ -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(() =>
|
||||
|
@ -21,14 +21,10 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Configuration.System;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.ViewModels
|
||||
@ -138,6 +134,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
public bool ExpandDramSize { get; set; }
|
||||
public bool EnableShaderCache { get; set; }
|
||||
public bool EnableTextureRecompression { get; set; }
|
||||
public bool EnableMacroHLE { get; set; }
|
||||
public bool EnableFileLog { get; set; }
|
||||
public bool EnableStub { get; set; }
|
||||
public bool EnableInfo { get; set; }
|
||||
@ -295,8 +292,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
if (_validTzRegions.Contains(location))
|
||||
{
|
||||
TimeZone = location;
|
||||
|
||||
OnPropertyChanged(nameof(TimeZone));
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,6 +336,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
ExpandDramSize = config.System.ExpandRam;
|
||||
EnableShaderCache = config.Graphics.EnableShaderCache;
|
||||
EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
|
||||
EnableMacroHLE = config.Graphics.EnableMacroHLE;
|
||||
EnableFileLog = config.Logger.EnableFileLog;
|
||||
EnableStub = config.Logger.EnableStub;
|
||||
EnableInfo = config.Logger.EnableInfo;
|
||||
@ -424,6 +420,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||
config.Graphics.EnableVsync.Value = EnableVsync;
|
||||
config.Graphics.EnableShaderCache.Value = EnableShaderCache;
|
||||
config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
|
||||
config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
|
||||
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
|
||||
config.System.EnablePtc.Value = EnablePptc;
|
||||
config.System.EnableInternetAccess.Value = EnableInternetAccess;
|
||||
|
@ -164,7 +164,7 @@
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="pl_PL"
|
||||
Header="Polish" />
|
||||
Header="Polski" />
|
||||
<MenuItem
|
||||
Command="{ReflectionBinding ChangeLanguage}"
|
||||
CommandParameter="ru_RU"
|
||||
|
@ -23,6 +23,7 @@ using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
@ -247,7 +248,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
RendererControl.CreateVulkan();
|
||||
}
|
||||
|
||||
|
||||
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
|
||||
|
||||
if (!AppHost.LoadGuestApplication().Result)
|
||||
@ -432,7 +433,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
// Consider removing this at some point in the future when we don't need to worry about old saves.
|
||||
VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient);
|
||||
|
||||
AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, Program.CommandLineProfile);
|
||||
AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile);
|
||||
|
||||
VirtualFileSystem.ReloadKeySet();
|
||||
|
||||
@ -525,6 +526,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
||||
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
|
||||
GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
|
||||
GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
|
||||
}
|
||||
|
||||
public void LoadHotKeys()
|
||||
|
@ -568,6 +568,10 @@
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding EnableMacroHLE}"
|
||||
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
|
||||
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
|
@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
||||
InitializeComponent();
|
||||
Load();
|
||||
|
||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
|
||||
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
|
||||
MultiBinding tzMultiBinding = new() { Converter = converter };
|
||||
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
|
||||
tzMultiBinding.Bindings.Add(new Binding("Location"));
|
||||
|
@ -53,5 +53,11 @@ namespace Ryujinx.Common
|
||||
|
||||
writer.Write(data);
|
||||
}
|
||||
|
||||
public static void Write(this BinaryWriter writer, UInt128 value)
|
||||
{
|
||||
writer.Write((ulong)value);
|
||||
writer.Write((ulong)(value >> 64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -129,7 +129,7 @@ namespace Ryujinx.Common
|
||||
|
||||
private static (Assembly, string) ResolveManifestPath(string filename)
|
||||
{
|
||||
var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (segments.Length >= 2)
|
||||
{
|
||||
|
@ -7,19 +7,19 @@ namespace Ryujinx.Common.Utilities
|
||||
public static class SpanHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<T> CreateSpan<T>(ref T reference, int length)
|
||||
public static Span<T> CreateSpan<T>(scoped ref T reference, int length)
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref reference, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
|
||||
public static Span<T> AsSpan<T>(scoped ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateSpan(ref reference, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
|
||||
public static Span<TSpan> AsSpan<TStruct, TSpan>(scoped ref TStruct reference)
|
||||
where TStruct : unmanaged where TSpan : unmanaged
|
||||
{
|
||||
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||
@ -27,25 +27,25 @@ namespace Ryujinx.Common.Utilities
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
||||
public static Span<byte> AsByteSpan<T>(scoped ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
|
||||
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(scoped ref T reference, int length)
|
||||
{
|
||||
return MemoryMarshal.CreateReadOnlySpan(ref reference, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged
|
||||
public static ReadOnlySpan<T> AsReadOnlySpan<T>(scoped ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateReadOnlySpan(ref reference, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(ref TStruct reference)
|
||||
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(scoped ref TStruct reference)
|
||||
where TStruct : unmanaged where TSpan : unmanaged
|
||||
{
|
||||
return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||
@ -53,7 +53,7 @@ namespace Ryujinx.Common.Utilities
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(ref T reference) where T : unmanaged
|
||||
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(scoped ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||
}
|
||||
|
17
Ryujinx.Common/Utilities/UInt128Utils.cs
Normal file
17
Ryujinx.Common/Utilities/UInt128Utils.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public static class UInt128Utils
|
||||
{
|
||||
public static UInt128 FromHex(string hex)
|
||||
{
|
||||
return new UInt128((ulong)Convert.ToInt64(hex.Substring(0, 16), 16), (ulong)Convert.ToInt64(hex.Substring(16), 16));
|
||||
}
|
||||
|
||||
public static UInt128 CreateRandom()
|
||||
{
|
||||
return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -34,13 +34,14 @@ namespace Ryujinx.Graphics.GAL
|
||||
int firstIndex,
|
||||
int firstVertex,
|
||||
int firstInstance);
|
||||
void DrawIndexedIndirect(BufferRange indirectBuffer);
|
||||
void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
|
||||
void DrawIndirect(BufferRange indirectBuffer);
|
||||
void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
|
||||
void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion);
|
||||
|
||||
void EndTransformFeedback();
|
||||
|
||||
void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
|
||||
void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
|
||||
|
||||
void SetAlphaTest(bool enable, float reference, CompareOp op);
|
||||
|
||||
void SetBlendState(int index, BlendDescriptor blend);
|
||||
|
@ -141,16 +141,20 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
DrawCommand.Run(ref GetCommand<DrawCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndexed] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
DrawIndexedCommand.Run(ref GetCommand<DrawIndexedCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndexedIndirect] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
DrawIndexedIndirectCommand.Run(ref GetCommand<DrawIndexedIndirectCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndexedIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
DrawIndexedIndirectCountCommand.Run(ref GetCommand<DrawIndexedIndirectCountCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndirect] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
DrawIndirectCommand.Run(ref GetCommand<DrawIndirectCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
DrawIndirectCountCommand.Run(ref GetCommand<DrawIndirectCountCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawTexture] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
DrawTextureCommand.Run(ref GetCommand<DrawTextureCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.EndHostConditionalRendering] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
EndHostConditionalRenderingCommand.Run(renderer);
|
||||
_lookup[(int)CommandType.EndTransformFeedback] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
EndTransformFeedbackCommand.Run(ref GetCommand<EndTransformFeedbackCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.MultiDrawIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
MultiDrawIndirectCountCommand.Run(ref GetCommand<MultiDrawIndirectCountCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.MultiDrawIndexedIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
MultiDrawIndexedIndirectCountCommand.Run(ref GetCommand<MultiDrawIndexedIndirectCountCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetAlphaTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetAlphaTestCommand.Run(ref GetCommand<SetAlphaTestCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetBlendState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
|
@ -52,11 +52,13 @@
|
||||
DispatchCompute,
|
||||
Draw,
|
||||
DrawIndexed,
|
||||
DrawIndexedIndirect,
|
||||
DrawIndexedIndirectCount,
|
||||
DrawIndirect,
|
||||
DrawIndirectCount,
|
||||
DrawTexture,
|
||||
EndHostConditionalRendering,
|
||||
EndTransformFeedback,
|
||||
MultiDrawIndirectCount,
|
||||
MultiDrawIndexedIndirectCount,
|
||||
SetAlphaTest,
|
||||
SetBlendState,
|
||||
SetDepthBias,
|
||||
|
@ -0,0 +1,18 @@
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct DrawIndexedIndirectCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.DrawIndexedIndirect;
|
||||
private BufferRange _indirectBuffer;
|
||||
|
||||
public void Set(BufferRange indirectBuffer)
|
||||
{
|
||||
_indirectBuffer = indirectBuffer;
|
||||
}
|
||||
|
||||
public static void Run(ref DrawIndexedIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.DrawIndexedIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct MultiDrawIndexedIndirectCountCommand : IGALCommand
|
||||
struct DrawIndexedIndirectCountCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.MultiDrawIndexedIndirectCount;
|
||||
public CommandType CommandType => CommandType.DrawIndexedIndirectCount;
|
||||
private BufferRange _indirectBuffer;
|
||||
private BufferRange _parameterBuffer;
|
||||
private int _maxDrawCount;
|
||||
@ -16,9 +16,9 @@
|
||||
_stride = stride;
|
||||
}
|
||||
|
||||
public static void Run(ref MultiDrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
public static void Run(ref DrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.MultiDrawIndexedIndirectCount(
|
||||
renderer.Pipeline.DrawIndexedIndirectCount(
|
||||
threaded.Buffers.MapBufferRange(command._indirectBuffer),
|
||||
threaded.Buffers.MapBufferRange(command._parameterBuffer),
|
||||
command._maxDrawCount,
|
@ -0,0 +1,18 @@
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct DrawIndirectCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.DrawIndirect;
|
||||
private BufferRange _indirectBuffer;
|
||||
|
||||
public void Set(BufferRange indirectBuffer)
|
||||
{
|
||||
_indirectBuffer = indirectBuffer;
|
||||
}
|
||||
|
||||
public static void Run(ref DrawIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.DrawIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct MultiDrawIndirectCountCommand : IGALCommand
|
||||
struct DrawIndirectCountCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.MultiDrawIndirectCount;
|
||||
public CommandType CommandType => CommandType.DrawIndirectCount;
|
||||
private BufferRange _indirectBuffer;
|
||||
private BufferRange _parameterBuffer;
|
||||
private int _maxDrawCount;
|
||||
@ -16,9 +16,9 @@
|
||||
_stride = stride;
|
||||
}
|
||||
|
||||
public static void Run(ref MultiDrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
public static void Run(ref DrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.MultiDrawIndirectCount(
|
||||
renderer.Pipeline.DrawIndirectCount(
|
||||
threaded.Buffers.MapBufferRange(command._indirectBuffer),
|
||||
threaded.Buffers.MapBufferRange(command._parameterBuffer),
|
||||
command._maxDrawCount,
|
@ -83,6 +83,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirect(BufferRange indirectBuffer)
|
||||
{
|
||||
_renderer.New<DrawIndexedIndirectCommand>().Set(indirectBuffer);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
_renderer.New<DrawIndexedIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void DrawIndirect(BufferRange indirectBuffer)
|
||||
{
|
||||
_renderer.New<DrawIndirectCommand>().Set(indirectBuffer);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
_renderer.New<DrawIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
|
||||
{
|
||||
_renderer.New<DrawTextureCommand>().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion);
|
||||
@ -101,18 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
_renderer.New<MultiDrawIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
_renderer.New<MultiDrawIndexedIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetAlphaTest(bool enable, float reference, CompareOp op)
|
||||
{
|
||||
_renderer.New<SetAlphaTestCommand>().Set(enable, reference, op);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -94,6 +94,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||
{
|
||||
var memoryManager = _channel.MemoryManager;
|
||||
|
||||
// Since we're going to change the state, make sure any pending instanced draws are done.
|
||||
_3dEngine.PerformDeferredDraws();
|
||||
|
||||
// Make sure all pending uniform buffer data is written to memory.
|
||||
_3dEngine.FlushUboDirty();
|
||||
|
||||
uint qmdAddress = _state.State.SendPcasA;
|
||||
|
@ -62,10 +62,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
}
|
||||
}
|
||||
|
||||
if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||
// We don't consume the parameter buffer value, so we don't need to flush it.
|
||||
// Doing so improves performance if the value was written by a GPU shader.
|
||||
if (_hleFunction == MacroHLEFunctionName.DrawElementsIndirect)
|
||||
{
|
||||
context.GPFifo.SetFlushSkips(1);
|
||||
}
|
||||
else if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||
{
|
||||
// We don't consume the parameter buffer value, so we don't need to flush it.
|
||||
// Doing so improves performance if the value was written by a GPU shader.
|
||||
context.GPFifo.SetFlushSkips(2);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
private const int ColorStructSize = 0x40;
|
||||
private const int ZetaLayerCountOffset = 0x1230;
|
||||
|
||||
private const int IndirectDataEntrySize = 0x10;
|
||||
private const int IndirectIndexedDataEntrySize = 0x14;
|
||||
|
||||
private readonly GPFifoProcessor _processor;
|
||||
private readonly MacroHLEFunctionName _functionName;
|
||||
|
||||
@ -27,9 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
/// <summary>
|
||||
/// Creates a new instance of the HLE macro handler.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context the macro is being executed on</param>
|
||||
/// <param name="memoryManager">GPU memory manager</param>
|
||||
/// <param name="engine">3D engine where this macro is being called</param>
|
||||
/// <param name="processor">GPU GP FIFO command processor</param>
|
||||
/// <param name="functionName">Name of the HLE macro function to be called</param>
|
||||
public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
|
||||
{
|
||||
@ -55,12 +56,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
case MacroHLEFunctionName.ClearDepthStencil:
|
||||
ClearDepthStencil(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.DrawArraysInstanced:
|
||||
DrawArraysInstanced(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.DrawElementsInstanced:
|
||||
DrawElementsInstanced(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.DrawElementsIndirect:
|
||||
DrawElementsIndirect(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
|
||||
MultiDrawElementsIndirectCount(state, arg0);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(_functionName.ToString());
|
||||
}
|
||||
|
||||
// It should be empty at this point, but clear it just to be safe.
|
||||
Fifo.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -89,7 +102,118 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect multi-draw, with parameters from a GPU buffer.
|
||||
/// Performs a draw.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void DrawArraysInstanced(IDeviceState state, int arg0)
|
||||
{
|
||||
var topology = (PrimitiveTopology)arg0;
|
||||
|
||||
var count = FetchParam();
|
||||
var instanceCount = FetchParam();
|
||||
var firstVertex = FetchParam();
|
||||
var firstInstance = FetchParam();
|
||||
|
||||
if (ShouldSkipDraw(state, instanceCount.Word))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_processor.ThreedClass.Draw(
|
||||
topology,
|
||||
count.Word,
|
||||
instanceCount.Word,
|
||||
0,
|
||||
firstVertex.Word,
|
||||
firstInstance.Word,
|
||||
indexed: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void DrawElementsInstanced(IDeviceState state, int arg0)
|
||||
{
|
||||
var topology = (PrimitiveTopology)arg0;
|
||||
|
||||
var count = FetchParam();
|
||||
var instanceCount = FetchParam();
|
||||
var firstIndex = FetchParam();
|
||||
var firstVertex = FetchParam();
|
||||
var firstInstance = FetchParam();
|
||||
|
||||
if (ShouldSkipDraw(state, instanceCount.Word))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_processor.ThreedClass.Draw(
|
||||
topology,
|
||||
count.Word,
|
||||
instanceCount.Word,
|
||||
firstIndex.Word,
|
||||
firstVertex.Word,
|
||||
firstInstance.Word,
|
||||
indexed: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect indexed draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void DrawElementsIndirect(IDeviceState state, int arg0)
|
||||
{
|
||||
var topology = (PrimitiveTopology)arg0;
|
||||
|
||||
var count = FetchParam();
|
||||
var instanceCount = FetchParam();
|
||||
var firstIndex = FetchParam();
|
||||
var firstVertex = FetchParam();
|
||||
var firstInstance = FetchParam();
|
||||
|
||||
ulong indirectBufferGpuVa = count.GpuVa;
|
||||
|
||||
var bufferCache = _processor.MemoryManager.Physical.BufferCache;
|
||||
|
||||
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
|
||||
|
||||
if (useBuffer)
|
||||
{
|
||||
int indexCount = firstIndex.Word + count.Word;
|
||||
|
||||
_processor.ThreedClass.DrawIndirect(
|
||||
topology,
|
||||
indirectBufferAddress,
|
||||
0,
|
||||
1,
|
||||
IndirectIndexedDataEntrySize,
|
||||
indexCount,
|
||||
Threed.IndirectDrawType.DrawIndexedIndirect);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ShouldSkipDraw(state, instanceCount.Word))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_processor.ThreedClass.Draw(
|
||||
topology,
|
||||
count.Word,
|
||||
instanceCount.Word,
|
||||
firstIndex.Word,
|
||||
firstVertex.Word,
|
||||
firstInstance.Word,
|
||||
indexed: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect indexed multi-draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
@ -132,8 +256,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
return;
|
||||
}
|
||||
|
||||
int indirectBufferSize = maxDrawCount * stride;
|
||||
|
||||
ulong indirectBufferGpuVa = 0;
|
||||
int indexCount = 0;
|
||||
|
||||
@ -142,8 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
var count = FetchParam();
|
||||
var instanceCount = FetchParam();
|
||||
var firstIndex = FetchParam();
|
||||
var baseVertex = FetchParam();
|
||||
var baseInstance = FetchParam();
|
||||
var firstVertex = FetchParam();
|
||||
var firstInstance = FetchParam();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
@ -161,15 +283,32 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
}
|
||||
}
|
||||
|
||||
// It should be empty at this point, but clear it just to be safe.
|
||||
Fifo.Clear();
|
||||
|
||||
var bufferCache = _processor.MemoryManager.Physical.BufferCache;
|
||||
|
||||
var parameterBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4);
|
||||
var indirectBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize);
|
||||
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
|
||||
|
||||
_processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
|
||||
ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4);
|
||||
|
||||
_processor.ThreedClass.DrawIndirect(
|
||||
topology,
|
||||
indirectBufferAddress,
|
||||
parameterBufferAddress,
|
||||
maxDrawCount,
|
||||
stride,
|
||||
indexCount,
|
||||
Threed.IndirectDrawType.DrawIndexedIndirectCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the draw should be skipped, because the masked instance count is zero.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="instanceCount">Draw instance count</param>
|
||||
/// <returns>True if the draw should be skipped, false otherwise</returns>
|
||||
private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
|
||||
{
|
||||
return (Read(state, 0xd1b) & instanceCount) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -189,14 +328,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a GPU method call.
|
||||
/// Reads data from a GPU register.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="methAddr">Address, in words, of the method</param>
|
||||
/// <param name="value">Call argument</param>
|
||||
private static void Send(IDeviceState state, int methAddr, int value)
|
||||
/// <param name="reg">Register offset to read</param>
|
||||
/// <returns>GPU register value</returns>
|
||||
private static int Read(IDeviceState state, int reg)
|
||||
{
|
||||
state.Write(methAddr * 4, value);
|
||||
return state.Read(reg * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,9 @@
|
||||
None,
|
||||
ClearColor,
|
||||
ClearDepthStencil,
|
||||
DrawArraysInstanced,
|
||||
DrawElementsInstanced,
|
||||
DrawElementsIndirect,
|
||||
MultiDrawElementsIndirectCount
|
||||
}
|
||||
}
|
||||
|
@ -44,17 +44,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly TableEntry[] Table = new TableEntry[]
|
||||
private static readonly TableEntry[] _table = new TableEntry[]
|
||||
{
|
||||
new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
|
||||
new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
|
||||
new TableEntry(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
|
||||
new TableEntry(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
|
||||
new TableEntry(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
|
||||
new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the host supports all features required by the HLE macro.
|
||||
/// </summary>
|
||||
/// <param name="caps">Host capabilities</param>
|
||||
/// <param name="name">Name of the HLE macro to be checked</param>
|
||||
/// <returns>True if the host supports the HLE macro, false otherwise</returns>
|
||||
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
|
||||
{
|
||||
if (name == MacroHLEFunctionName.ClearColor ||
|
||||
name == MacroHLEFunctionName.ClearDepthStencil)
|
||||
name == MacroHLEFunctionName.ClearDepthStencil ||
|
||||
name == MacroHLEFunctionName.DrawArraysInstanced ||
|
||||
name == MacroHLEFunctionName.DrawElementsInstanced ||
|
||||
name == MacroHLEFunctionName.DrawElementsIndirect)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -77,15 +89,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
{
|
||||
var mc = MemoryMarshal.Cast<int, byte>(code);
|
||||
|
||||
for (int i = 0; i < Table.Length; i++)
|
||||
for (int i = 0; i < _table.Length; i++)
|
||||
{
|
||||
ref var entry = ref Table[i];
|
||||
ref var entry = ref _table[i];
|
||||
|
||||
var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length));
|
||||
if (hash == entry.Hash)
|
||||
{
|
||||
name = entry.Name;
|
||||
return IsMacroHLESupported(caps, name);
|
||||
if (IsMacroHLESupported(caps, entry.Name))
|
||||
{
|
||||
name = entry.Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
else
|
||||
{
|
||||
evt.Flush();
|
||||
return (memoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||
return (memoryManager.Read<ulong>(gpuVa, true) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
evt?.Flush();
|
||||
evt2?.Flush();
|
||||
|
||||
ulong x = memoryManager.Read<ulong>(gpuVa);
|
||||
ulong y = memoryManager.Read<ulong>(gpuVa + 16);
|
||||
ulong x = memoryManager.Read<ulong>(gpuVa, true);
|
||||
ulong y = memoryManager.Read<ulong>(gpuVa + 16, true);
|
||||
|
||||
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
@ -9,6 +10,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
class DrawManager
|
||||
{
|
||||
// Since we don't know the index buffer size for indirect draws,
|
||||
// we must assume a minimum and maximum size and use that for buffer data update purposes.
|
||||
private const int MinIndirectIndexCount = 0x10000;
|
||||
private const int MaxIndirectIndexCount = 0x4000000;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||
@ -28,6 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
private int _instanceIndex;
|
||||
|
||||
private const int VertexBufferFirstMethodOffset = 0x35d;
|
||||
private const int IndexBufferCountMethodOffset = 0x5f8;
|
||||
|
||||
/// <summary>
|
||||
@ -237,6 +244,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
_instanceIndex = 0;
|
||||
}
|
||||
|
||||
UpdateTopology(topology);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the current primitive topology if needed.
|
||||
/// </summary>
|
||||
/// <param name="topology">New primitive topology</param>
|
||||
private void UpdateTopology(PrimitiveTopology topology)
|
||||
{
|
||||
if (_drawState.Topology != topology || !_topologySet)
|
||||
{
|
||||
_context.Renderer.Pipeline.SetPrimitiveTopology(topology);
|
||||
@ -383,28 +399,27 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect multi-draw, with parameters from a GPU buffer.
|
||||
/// Performs a indexed or non-indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="indirectBuffer">GPU buffer with the draw parameters, such as count, first index, etc</param>
|
||||
/// <param name="parameterBuffer">GPU buffer with the draw count</param>
|
||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||
/// <param name="stride">Distance in bytes between each element on the <paramref name="indirectBuffer"/> array</param>
|
||||
public void MultiDrawIndirectCount(
|
||||
/// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
|
||||
/// <param name="instanceCount">Instance count</param>
|
||||
/// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
|
||||
/// <param name="firstVertex">First vertex on the vertex buffer</param>
|
||||
/// <param name="firstInstance">First instance</param>
|
||||
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
|
||||
public void Draw(
|
||||
ThreedClass engine,
|
||||
int indexCount,
|
||||
PrimitiveTopology topology,
|
||||
BufferRange indirectBuffer,
|
||||
BufferRange parameterBuffer,
|
||||
int maxDrawCount,
|
||||
int stride)
|
||||
int count,
|
||||
int instanceCount,
|
||||
int firstIndex,
|
||||
int firstVertex,
|
||||
int firstInstance,
|
||||
bool indexed)
|
||||
{
|
||||
engine.Write(IndexBufferCountMethodOffset * 4, indexCount);
|
||||
|
||||
_context.Renderer.Pipeline.SetPrimitiveTopology(topology);
|
||||
_drawState.Topology = topology;
|
||||
_topologySet = true;
|
||||
UpdateTopology(topology);
|
||||
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
@ -418,21 +433,133 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
return;
|
||||
}
|
||||
|
||||
_drawState.FirstIndex = _state.State.IndexBufferState.First;
|
||||
_drawState.IndexCount = indexCount;
|
||||
|
||||
engine.UpdateState();
|
||||
|
||||
if (_drawState.DrawIndexed)
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.MultiDrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
_drawState.FirstIndex = firstIndex;
|
||||
_drawState.IndexCount = count;
|
||||
_state.State.FirstVertex = (uint)firstVertex;
|
||||
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.MultiDrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
_state.State.VertexBufferDrawState.First = firstVertex;
|
||||
_state.State.VertexBufferDrawState.Count = count;
|
||||
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
|
||||
}
|
||||
|
||||
_state.State.FirstInstance = (uint)firstInstance;
|
||||
|
||||
_drawState.DrawIndexed = indexed;
|
||||
_drawState.HasConstantBufferDrawParameters = true;
|
||||
|
||||
engine.UpdateState();
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
|
||||
_state.State.FirstVertex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
|
||||
}
|
||||
|
||||
_state.State.FirstInstance = 0;
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
_drawState.HasConstantBufferDrawParameters = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
|
||||
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
|
||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
|
||||
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
|
||||
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
||||
public void DrawIndirect(
|
||||
ThreedClass engine,
|
||||
PrimitiveTopology topology,
|
||||
ulong indirectBufferAddress,
|
||||
ulong parameterBufferAddress,
|
||||
int maxDrawCount,
|
||||
int stride,
|
||||
int indexCount,
|
||||
IndirectDrawType drawType)
|
||||
{
|
||||
UpdateTopology(topology);
|
||||
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
_channel.MemoryManager,
|
||||
_state.State.RenderEnableAddress,
|
||||
_state.State.RenderEnableCondition);
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.False)
|
||||
{
|
||||
_drawState.DrawIndexed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PhysicalMemory memory = _channel.MemoryManager.Physical;
|
||||
|
||||
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
|
||||
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount);
|
||||
_drawState.FirstIndex = 0;
|
||||
_drawState.IndexCount = indexCount;
|
||||
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
|
||||
}
|
||||
|
||||
_drawState.DrawIndexed = indexed;
|
||||
_drawState.DrawIndirect = true;
|
||||
_drawState.HasConstantBufferDrawParameters = true;
|
||||
|
||||
engine.UpdateState();
|
||||
|
||||
if (hasCount)
|
||||
{
|
||||
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride);
|
||||
var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4);
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride);
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndirect(indirectBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
_drawState.DrawIndirect = false;
|
||||
_drawState.HasConstantBufferDrawParameters = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
|
@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
public bool DrawIndexed;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the next draw will be a indirect draw.
|
||||
/// </summary>
|
||||
public bool DrawIndirect;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if any of the currently used vertex shaders reads the instance ID.
|
||||
/// </summary>
|
||||
@ -32,6 +37,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
public bool IsAnyVbInstanced;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
|
||||
/// </summary>
|
||||
public bool HasConstantBufferDrawParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Primitive topology for the next draw.
|
||||
/// </summary>
|
||||
|
38
Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs
Normal file
38
Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs
Normal file
@ -0,0 +1,38 @@
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Indirect draw type, which can be indexed or non-indexed, with or without a draw count.
|
||||
/// </summary>
|
||||
enum IndirectDrawType
|
||||
{
|
||||
/// <summary>
|
||||
/// Non-indexed draw without draw count.
|
||||
/// </summary>
|
||||
DrawIndirect = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indexed draw without draw count.
|
||||
/// </summary>
|
||||
DrawIndexedIndirect = Indexed,
|
||||
|
||||
/// <summary>
|
||||
/// Non-indexed draw with draw count.
|
||||
/// </summary>
|
||||
DrawIndirectCount = Count,
|
||||
|
||||
/// <summary>
|
||||
/// Indexed draw with draw count.
|
||||
/// </summary>
|
||||
DrawIndexedIndirectCount = Indexed | Count,
|
||||
|
||||
/// <summary>
|
||||
/// Indexed flag.
|
||||
/// </summary>
|
||||
Indexed = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Draw count flag.
|
||||
/// </summary>
|
||||
Count = 1 << 1
|
||||
}
|
||||
}
|
@ -34,10 +34,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
private ProgramPipelineState _pipeline;
|
||||
|
||||
private bool _vsUsesDrawParameters;
|
||||
private bool _vtgWritesRtLayer;
|
||||
private byte _vsClipDistancesWritten;
|
||||
|
||||
private bool _prevDrawIndexed;
|
||||
private bool _prevDrawIndirect;
|
||||
private IndexType _prevIndexType;
|
||||
private uint _prevFirstVertex;
|
||||
private bool _prevTfEnable;
|
||||
@ -210,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
// of the shader for the new state.
|
||||
if (_shaderSpecState != null)
|
||||
{
|
||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false))
|
||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false))
|
||||
{
|
||||
ForceShaderUpdate();
|
||||
}
|
||||
@ -237,6 +239,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
_prevDrawIndexed = _drawState.DrawIndexed;
|
||||
}
|
||||
|
||||
// Some draw parameters are used to restrict the vertex buffer size,
|
||||
// but they can't be used on indirect draws because their values are unknown in this case.
|
||||
// When switching between indirect and non-indirect draw, we need to
|
||||
// make sure the vertex buffer sizes are still correct.
|
||||
if (_drawState.DrawIndirect != _prevDrawIndirect)
|
||||
{
|
||||
_updateTracker.ForceDirty(VertexBufferStateIndex);
|
||||
}
|
||||
|
||||
// In some cases, the index type is also used to guess the
|
||||
// vertex buffer size, so we must update it if the type changed too.
|
||||
if (_drawState.DrawIndexed &&
|
||||
@ -938,6 +949,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
_drawState.IsAnyVbInstanced = false;
|
||||
|
||||
bool drawIndexed = _drawState.DrawIndexed;
|
||||
bool drawIndirect = _drawState.DrawIndirect;
|
||||
|
||||
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
||||
{
|
||||
var vertexBuffer = _state.State.VertexBufferState[index];
|
||||
@ -965,14 +979,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
ulong vbSize = endAddress.Pack() - address + 1;
|
||||
ulong size;
|
||||
|
||||
if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced)
|
||||
if (_drawState.IbStreamer.HasInlineIndexData || drawIndexed || stride == 0 || instanced)
|
||||
{
|
||||
// This size may be (much) larger than the real vertex buffer size.
|
||||
// Avoid calculating it this way, unless we don't have any other option.
|
||||
|
||||
size = vbSize;
|
||||
|
||||
if (stride > 0 && indexTypeSmall && _drawState.DrawIndexed && !instanced)
|
||||
if (stride > 0 && indexTypeSmall && drawIndexed && !drawIndirect && !instanced)
|
||||
{
|
||||
// If the index type is a small integer type, then we might be still able
|
||||
// to reduce the vertex buffer size based on the maximum possible index value.
|
||||
@ -1207,6 +1221,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
|
||||
|
||||
_drawState.VsUsesInstanceId = gs.Shaders[1]?.Info.UsesInstanceId ?? false;
|
||||
_vsUsesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
|
||||
_vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0;
|
||||
|
||||
if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
|
||||
@ -1222,6 +1237,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates bindings consumed by the shader stage on the texture and buffer managers.
|
||||
/// </summary>
|
||||
/// <param name="stage">Shader stage to have the bindings updated</param>
|
||||
/// <param name="info">Shader stage bindings info</param>
|
||||
private void UpdateStageBindings(int stage, ShaderProgramInfo info)
|
||||
{
|
||||
_currentProgramInfo[stage] = info;
|
||||
@ -1340,7 +1360,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
_state.State.AlphaTestEnable,
|
||||
_state.State.AlphaTestFunc,
|
||||
_state.State.AlphaTestRef,
|
||||
ref attributeTypes);
|
||||
ref attributeTypes,
|
||||
_drawState.HasConstantBufferDrawParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -497,6 +497,50 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed or non-indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
|
||||
/// <param name="instanceCount">Instance count</param>
|
||||
/// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
|
||||
/// <param name="firstVertex">First vertex on the vertex buffer</param>
|
||||
/// <param name="firstInstance">First instance</param>
|
||||
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
|
||||
public void Draw(
|
||||
PrimitiveTopology topology,
|
||||
int count,
|
||||
int instanceCount,
|
||||
int firstIndex,
|
||||
int firstVertex,
|
||||
int firstInstance,
|
||||
bool indexed)
|
||||
{
|
||||
_drawManager.Draw(this, topology, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
|
||||
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
|
||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
|
||||
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
|
||||
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
||||
public void DrawIndirect(
|
||||
PrimitiveTopology topology,
|
||||
ulong indirectBufferAddress,
|
||||
ulong parameterBufferAddress,
|
||||
int maxDrawCount,
|
||||
int stride,
|
||||
int indexCount,
|
||||
IndirectDrawType drawType)
|
||||
{
|
||||
_drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared can also specified with the arguments.
|
||||
@ -507,25 +551,5 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
_drawManager.Clear(this, argument, layerCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect multi-draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="indexCount">Index Buffer Count</param>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="indirectBuffer">GPU buffer with the draw parameters, such as count, first index, etc</param>
|
||||
/// <param name="parameterBuffer">GPU buffer with the draw count</param>
|
||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||
/// <param name="stride">Distance in bytes between each element on the <paramref name="indirectBuffer"/> array</param>
|
||||
public void MultiDrawIndirectCount(
|
||||
int indexCount,
|
||||
PrimitiveTopology topology,
|
||||
BufferRange indirectBuffer,
|
||||
BufferRange parameterBuffer,
|
||||
int maxDrawCount,
|
||||
int stride)
|
||||
{
|
||||
_drawManager.MultiDrawIndirectCount(this, indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -708,11 +708,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
else
|
||||
{
|
||||
bool dataMatches = _currentData != null && data.SequenceEqual(_currentData);
|
||||
_currentData = data.ToArray();
|
||||
if (dataMatches)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentData = data.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,10 +413,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateCachedBuffer(
|
||||
int stageIndex,
|
||||
ref int cachedTextureBufferIndex,
|
||||
ref int cachedSamplerBufferIndex,
|
||||
ref ReadOnlySpan<int> cachedTextureBuffer,
|
||||
ref ReadOnlySpan<int> cachedSamplerBuffer,
|
||||
scoped ref int cachedTextureBufferIndex,
|
||||
scoped ref int cachedSamplerBufferIndex,
|
||||
scoped ref ReadOnlySpan<int> cachedTextureBuffer,
|
||||
scoped ref ReadOnlySpan<int> cachedSamplerBuffer,
|
||||
int textureBufferIndex,
|
||||
int samplerBufferIndex)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
private Buffer[] _bufferOverlaps;
|
||||
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
|
||||
|
||||
public event Action NotifyBuffersModified;
|
||||
|
||||
@ -45,6 +46,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
|
||||
|
||||
_dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
|
||||
|
||||
// There are a lot more entries on the modified cache, so it is separate from the one for ForceDirty.
|
||||
_modifiedCache = new Dictionary<ulong, BufferCacheEntry>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -145,6 +149,30 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
result.Buffer.ForceDirty(result.Address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given buffer range has been GPU modifed.
|
||||
/// </summary>
|
||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
/// <returns>True if modified, false otherwise</returns>
|
||||
public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
|
||||
{
|
||||
if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < gpuVa + size ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
|
||||
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
|
||||
|
||||
_modifiedCache[gpuVa] = result;
|
||||
}
|
||||
|
||||
outAddr = result.Address;
|
||||
|
||||
return result.Buffer.IsModified(result.Address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new buffer for the specified range, if needed.
|
||||
/// If a buffer where this range can be fully contained already exists,
|
||||
@ -326,18 +354,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
buffer.SignalModified(address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a buffer sub-range for a given GPU memory range.
|
||||
/// </summary>
|
||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
/// <returns>The buffer sub-range for the given range</returns>
|
||||
public BufferRange GetGpuBufferRange(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||
{
|
||||
return GetBufferRange(TranslateAndCreateBuffer(memoryManager, gpuVa, size), size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a buffer sub-range starting at a given memory address.
|
||||
/// </summary>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -129,6 +129,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
return _oldSpecState.ConstantBufferUse[_stageIndex];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryHasConstantBufferDrawParameters()
|
||||
{
|
||||
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public InputTopology QueryPrimitiveTopology()
|
||||
{
|
||||
|
@ -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 = 3732;
|
||||
private const uint CodeGenVersion = 3747;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
@ -159,6 +159,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
/// Bit mask of the render target components written by the fragment stage.
|
||||
/// </summary>
|
||||
public int FragmentOutputMap;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the vertex shader accesses draw parameters.
|
||||
/// </summary>
|
||||
public bool UsesDrawParameters;
|
||||
}
|
||||
|
||||
private readonly DiskCacheGuestStorage _guestStorage;
|
||||
@ -771,6 +776,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
images,
|
||||
dataInfo.Stage,
|
||||
dataInfo.UsesInstanceId,
|
||||
dataInfo.UsesDrawParameters,
|
||||
dataInfo.UsesRtLayer,
|
||||
dataInfo.ClipDistancesWritten,
|
||||
dataInfo.FragmentOutputMap);
|
||||
@ -796,6 +802,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
dataInfo.ImagesCount = (ushort)info.Images.Count;
|
||||
dataInfo.Stage = info.Stage;
|
||||
dataInfo.UsesInstanceId = info.UsesInstanceId;
|
||||
dataInfo.UsesDrawParameters = info.UsesDrawParameters;
|
||||
dataInfo.UsesRtLayer = info.UsesRtLayer;
|
||||
dataInfo.ClipDistancesWritten = info.ClipDistancesWritten;
|
||||
dataInfo.FragmentOutputMap = info.FragmentOutputMap;
|
||||
|
@ -139,6 +139,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
return useMask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryHasConstantBufferDrawParameters()
|
||||
{
|
||||
return _state.GraphicsState.HasConstantBufferDrawParameters;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public InputTopology QueryPrimitiveTopology()
|
||||
{
|
||||
|
@ -77,6 +77,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
public Array32<AttributeType> AttributeTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
|
||||
/// </summary>
|
||||
public readonly bool HasConstantBufferDrawParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU graphics state.
|
||||
/// </summary>
|
||||
@ -93,6 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param>
|
||||
/// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param>
|
||||
/// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
|
||||
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
|
||||
public GpuChannelGraphicsState(
|
||||
bool earlyZForce,
|
||||
PrimitiveTopology topology,
|
||||
@ -106,7 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
bool alphaTestEnable,
|
||||
CompareOp alphaTestCompare,
|
||||
float alphaTestReference,
|
||||
ref Array32<AttributeType> attributeTypes)
|
||||
ref Array32<AttributeType> attributeTypes,
|
||||
bool hasConstantBufferDrawParameters)
|
||||
{
|
||||
EarlyZForce = earlyZForce;
|
||||
Topology = topology;
|
||||
@ -121,6 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
AlphaTestCompare = alphaTestCompare;
|
||||
AlphaTestReference = alphaTestReference;
|
||||
AttributeTypes = attributeTypes;
|
||||
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
|
||||
}
|
||||
}
|
||||
}
|
@ -350,7 +350,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable
|
||||
/// <param name="item">The item on the table, if found, otherwise unmodified</param>
|
||||
/// <param name="data">The data on the table, if found, otherwise unmodified</param>
|
||||
/// <returns>Table lookup result</returns>
|
||||
public SearchResult TryFindItem(ref SmartDataAccessor dataAccessor, int size, ref T item, ref byte[] data)
|
||||
public SearchResult TryFindItem(scoped ref SmartDataAccessor dataAccessor, int size, scoped ref T item, scoped ref byte[] data)
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable
|
||||
/// <param name="item">The item on the table, if found, otherwise unmodified</param>
|
||||
/// <param name="data">The data on the table, if found, otherwise unmodified</param>
|
||||
/// <returns>Table lookup result</returns>
|
||||
public PartitionHashTable<T>.SearchResult TryFindItem(ref SmartDataAccessor dataAccessor, ref T item, ref byte[] data)
|
||||
public PartitionHashTable<T>.SearchResult TryFindItem(scoped ref SmartDataAccessor dataAccessor, scoped ref T item, scoped ref byte[] data)
|
||||
{
|
||||
return _table.TryFindItem(ref dataAccessor, Size, ref item, ref data);
|
||||
}
|
||||
|
@ -520,7 +520,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
}
|
||||
|
||||
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true);
|
||||
bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false;
|
||||
|
||||
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -35,7 +35,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true))
|
||||
bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false;
|
||||
|
||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true))
|
||||
{
|
||||
program = entry;
|
||||
return true;
|
||||
|
@ -481,9 +481,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="poolState">Texture pool state</param>
|
||||
/// <param name="graphicsState">Graphics state</param>
|
||||
/// <param name="usesDrawParameters">Indicates whether the vertex shader accesses draw parameters</param>
|
||||
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||
/// <returns>True if the state matches, false otherwise</returns>
|
||||
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState, bool checkTextures)
|
||||
public bool MatchesGraphics(
|
||||
GpuChannel channel,
|
||||
GpuChannelPoolState poolState,
|
||||
GpuChannelGraphicsState graphicsState,
|
||||
bool usesDrawParameters,
|
||||
bool checkTextures)
|
||||
{
|
||||
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
|
||||
{
|
||||
@ -520,6 +526,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
return false;
|
||||
}
|
||||
|
||||
if (usesDrawParameters && graphicsState.HasConstantBufferDrawParameters != GraphicsState.HasConstantBufferDrawParameters)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Matches(channel, poolState, checkTextures, isCompute: false);
|
||||
}
|
||||
|
||||
@ -552,11 +563,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
private static void UpdateCachedBuffer(
|
||||
GpuChannel channel,
|
||||
bool isCompute,
|
||||
ref int cachedTextureBufferIndex,
|
||||
ref int cachedSamplerBufferIndex,
|
||||
ref ReadOnlySpan<int> cachedTextureBuffer,
|
||||
ref ReadOnlySpan<int> cachedSamplerBuffer,
|
||||
ref int cachedStageIndex,
|
||||
scoped ref int cachedTextureBufferIndex,
|
||||
scoped ref int cachedSamplerBufferIndex,
|
||||
scoped ref ReadOnlySpan<int> cachedTextureBuffer,
|
||||
scoped ref ReadOnlySpan<int> cachedSamplerBuffer,
|
||||
scoped ref int cachedStageIndex,
|
||||
int textureBufferIndex,
|
||||
int samplerBufferIndex,
|
||||
int stageIndex)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -34,5 +34,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
GL.ActiveTexture(TextureUnit.Texture0 + unit);
|
||||
GL.BindTexture(target, Handle);
|
||||
}
|
||||
|
||||
public static void ClearBinding(int unit)
|
||||
{
|
||||
GL.ActiveTexture(TextureUnit.Texture0 + unit);
|
||||
GL.BindTextureUnit(unit, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
|
||||
if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
|
||||
{
|
||||
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1);
|
||||
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1);
|
||||
}
|
||||
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
|
||||
{
|
||||
|
@ -586,6 +586,95 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirect(BufferRange indirectBuffer)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
PreDrawVbUnbounded();
|
||||
|
||||
_vertexArray.SetRangeOfIndexBuffer();
|
||||
|
||||
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
|
||||
|
||||
GL.DrawElementsIndirect(_primitiveType, _elementsType, (IntPtr)indirectBuffer.Offset);
|
||||
|
||||
_vertexArray.RestoreIndexBuffer();
|
||||
|
||||
PostDraw();
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
PreDrawVbUnbounded();
|
||||
|
||||
_vertexArray.SetRangeOfIndexBuffer();
|
||||
|
||||
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
|
||||
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
|
||||
|
||||
GL.MultiDrawElementsIndirectCount(
|
||||
_primitiveType,
|
||||
(All)_elementsType,
|
||||
(IntPtr)indirectBuffer.Offset,
|
||||
(IntPtr)parameterBuffer.Offset,
|
||||
maxDrawCount,
|
||||
stride);
|
||||
|
||||
_vertexArray.RestoreIndexBuffer();
|
||||
|
||||
PostDraw();
|
||||
}
|
||||
|
||||
public void DrawIndirect(BufferRange indirectBuffer)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
PreDrawVbUnbounded();
|
||||
|
||||
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
|
||||
|
||||
GL.DrawArraysIndirect(_primitiveType, (IntPtr)indirectBuffer.Offset);
|
||||
|
||||
PostDraw();
|
||||
}
|
||||
|
||||
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
PreDrawVbUnbounded();
|
||||
|
||||
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
|
||||
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
|
||||
|
||||
GL.MultiDrawArraysIndirectCount(
|
||||
_primitiveType,
|
||||
(IntPtr)indirectBuffer.Offset,
|
||||
(IntPtr)parameterBuffer.Offset,
|
||||
maxDrawCount,
|
||||
stride);
|
||||
|
||||
PostDraw();
|
||||
}
|
||||
|
||||
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
|
||||
{
|
||||
if (texture is TextureView view && sampler is Sampler samp)
|
||||
@ -683,57 +772,6 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_tfEnabled = false;
|
||||
}
|
||||
|
||||
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
PreDrawVbUnbounded();
|
||||
|
||||
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
|
||||
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
|
||||
|
||||
GL.MultiDrawArraysIndirectCount(
|
||||
_primitiveType,
|
||||
(IntPtr)indirectBuffer.Offset,
|
||||
(IntPtr)parameterBuffer.Offset,
|
||||
maxDrawCount,
|
||||
stride);
|
||||
|
||||
PostDraw();
|
||||
}
|
||||
|
||||
public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
PreDrawVbUnbounded();
|
||||
|
||||
_vertexArray.SetRangeOfIndexBuffer();
|
||||
|
||||
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
|
||||
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
|
||||
|
||||
GL.MultiDrawElementsIndirectCount(
|
||||
_primitiveType,
|
||||
(All)_elementsType,
|
||||
(IntPtr)indirectBuffer.Offset,
|
||||
(IntPtr)parameterBuffer.Offset,
|
||||
maxDrawCount,
|
||||
stride);
|
||||
|
||||
_vertexArray.RestoreIndexBuffer();
|
||||
|
||||
PostDraw();
|
||||
}
|
||||
|
||||
public void SetAlphaTest(bool enable, float reference, CompareOp op)
|
||||
{
|
||||
if (!enable)
|
||||
@ -919,6 +957,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
GL.BindImageTexture(binding, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1275,6 +1314,10 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
((TextureBase)texture).Bind(binding);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TextureBase.ClearBinding(binding);
|
||||
}
|
||||
|
||||
Sampler glSampler = (Sampler)sampler;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -10,12 +10,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
public StructuredFunction CurrentFunction { get; set; }
|
||||
|
||||
public StructuredProgramInfo Info { get; }
|
||||
|
||||
public ShaderConfig Config { get; }
|
||||
|
||||
public OperandManager OperandManager { get; }
|
||||
|
||||
private readonly StructuredProgramInfo _info;
|
||||
|
||||
private readonly StringBuilder _sb;
|
||||
|
||||
private int _level;
|
||||
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
public CodeGenContext(StructuredProgramInfo info, ShaderConfig config)
|
||||
{
|
||||
_info = info;
|
||||
Info = info;
|
||||
Config = config;
|
||||
|
||||
OperandManager = new OperandManager();
|
||||
@ -72,19 +72,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
public StructuredFunction GetFunction(int id)
|
||||
{
|
||||
return _info.Functions[id];
|
||||
}
|
||||
|
||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
|
||||
{
|
||||
int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component;
|
||||
return _info.TransformFeedbackOutputs[index];
|
||||
}
|
||||
|
||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int location)
|
||||
{
|
||||
int index = location / 4;
|
||||
return _info.TransformFeedbackOutputs[index];
|
||||
return Info.Functions[id];
|
||||
}
|
||||
|
||||
private void UpdateIndentation()
|
||||
|
@ -46,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
context.AppendLine("#extension GL_ARB_shader_draw_parameters : enable");
|
||||
}
|
||||
|
||||
context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable");
|
||||
}
|
||||
|
||||
@ -210,7 +215,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
||||
{
|
||||
var tfOutput = context.GetTransformFeedbackOutput(AttributeConsts.PositionX);
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex");
|
||||
@ -520,7 +525,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
private static void DeclareInputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
||||
{
|
||||
foreach (int attr in attrs.OrderBy(x => x))
|
||||
foreach (int attr in attrs.Order())
|
||||
{
|
||||
DeclareInputAttributePerPatch(context, attr);
|
||||
}
|
||||
@ -604,19 +609,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
|
||||
{
|
||||
for (int c = 0; c < 4; c++)
|
||||
int attrOffset = AttributeConsts.UserAttributeBase + attr * 16;
|
||||
int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
|
||||
|
||||
if (components > 1)
|
||||
{
|
||||
char swzMask = "xyzw"[c];
|
||||
string type = components switch
|
||||
{
|
||||
2 => "vec2",
|
||||
3 => "vec3",
|
||||
4 => "vec4",
|
||||
_ => "float"
|
||||
};
|
||||
|
||||
string xfb = string.Empty;
|
||||
|
||||
var tfOutput = context.GetTransformFeedbackOutput(attr, c);
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
|
||||
}
|
||||
|
||||
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
|
||||
context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
char swzMask = "xyzw"[c];
|
||||
|
||||
string xfb = string.Empty;
|
||||
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
|
||||
}
|
||||
|
||||
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -627,7 +658,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
|
||||
{
|
||||
foreach (int attr in attrs.OrderBy(x => x))
|
||||
foreach (int attr in attrs.Order())
|
||||
{
|
||||
DeclareOutputAttributePerPatch(context, attr);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute())
|
||||
{
|
||||
bool perPatch = operand.Type == OperandType.AttributePerPatch;
|
||||
dest = OperandManager.GetOutAttributeName(operand.Value, context.Config, perPatch);
|
||||
dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
}
|
||||
else if (node is AstOperand operand)
|
||||
{
|
||||
return context.OperandManager.GetExpression(operand, context.Config);
|
||||
return context.OperandManager.GetExpression(context, operand);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
|
||||
|
@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||
{
|
||||
int attrOffset = baseAttr.Value + (operand.Value << 2);
|
||||
return OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: false, indexExpr);
|
||||
return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -332,7 +332,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||
{
|
||||
int attrOffset = baseAttr.Value + (operand.Value << 2);
|
||||
attrName = OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: true);
|
||||
attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -48,6 +48,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
|
||||
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
|
||||
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
|
||||
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) },
|
||||
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) },
|
||||
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) },
|
||||
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) },
|
||||
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) },
|
||||
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
|
||||
|
||||
// Special.
|
||||
@ -99,15 +104,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
return name;
|
||||
}
|
||||
|
||||
public string GetExpression(AstOperand operand, ShaderConfig config)
|
||||
public string GetExpression(CodeGenContext context, AstOperand operand)
|
||||
{
|
||||
return operand.Type switch
|
||||
{
|
||||
OperandType.Argument => GetArgumentName(operand.Value),
|
||||
OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false),
|
||||
OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true),
|
||||
OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false),
|
||||
OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true),
|
||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, config),
|
||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
|
||||
OperandType.LocalVariable => _locals[operand],
|
||||
OperandType.Undefined => DefaultNames.UndefinedName,
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||
@ -149,13 +154,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
||||
}
|
||||
|
||||
public static string GetOutAttributeName(int value, ShaderConfig config, bool perPatch)
|
||||
public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch)
|
||||
{
|
||||
return GetAttributeName(value, config, perPatch, isOutAttr: true);
|
||||
return GetAttributeName(context, value, perPatch, isOutAttr: true);
|
||||
}
|
||||
|
||||
public static string GetAttributeName(int value, ShaderConfig config, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
|
||||
public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
|
||||
{
|
||||
ShaderConfig config = context.Config;
|
||||
|
||||
if ((value & AttributeConsts.LoadOutputMask) != 0)
|
||||
{
|
||||
isOutAttr = true;
|
||||
@ -188,6 +195,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
int attrOffset = value;
|
||||
value -= AttributeConsts.UserAttributeBase;
|
||||
|
||||
string prefix = isOutAttr
|
||||
@ -211,14 +219,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
((config.LastInVertexPipeline && isOutAttr) ||
|
||||
(config.Stage == ShaderStage.Fragment && !isOutAttr)))
|
||||
{
|
||||
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
||||
int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
|
||||
string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}";
|
||||
|
||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
||||
{
|
||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
||||
}
|
||||
|
||||
return name;
|
||||
return components > 1 ? name + '.' + swzMask : name;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
private const uint SpirvVersionRevision = 0;
|
||||
private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision;
|
||||
|
||||
private readonly StructuredProgramInfo _info;
|
||||
public StructuredProgramInfo Info { get; }
|
||||
|
||||
public ShaderConfig Config { get; }
|
||||
|
||||
@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
GeneratorPool<Instruction> instPool,
|
||||
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
|
||||
{
|
||||
_info = info;
|
||||
Info = info;
|
||||
Config = config;
|
||||
|
||||
if (config.Stage == ShaderStage.Geometry)
|
||||
@ -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);
|
||||
}
|
||||
@ -300,6 +317,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
attrOffset = attr;
|
||||
type = elemType;
|
||||
|
||||
if (Config.LastInPipeline && isOutAttr)
|
||||
{
|
||||
int components = Info.GetTransformFeedbackOutputComponents(attr);
|
||||
|
||||
if (components > 1)
|
||||
{
|
||||
attrOffset &= ~0xf;
|
||||
type = AggregateType.Vector | AggregateType.FP32;
|
||||
attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset];
|
||||
@ -308,12 +337,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
|
||||
{
|
||||
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable;
|
||||
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 +373,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);
|
||||
}
|
||||
@ -485,18 +548,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
return _functions[funcIndex];
|
||||
}
|
||||
|
||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
|
||||
{
|
||||
int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component;
|
||||
return _info.TransformFeedbackOutputs[index];
|
||||
}
|
||||
|
||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int location)
|
||||
{
|
||||
int index = location / 4;
|
||||
return _info.TransformFeedbackOutputs[index];
|
||||
}
|
||||
|
||||
public Instruction GetType(AggregateType type, int length = 1)
|
||||
{
|
||||
if (type.HasFlag(AggregateType.Array))
|
||||
|
@ -440,11 +440,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
PixelImap iq = PixelImap.Unused;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment &&
|
||||
attr >= AttributeConsts.UserAttributeBase &&
|
||||
attr < AttributeConsts.UserAttributeEnd)
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
|
||||
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
|
||||
}
|
||||
else
|
||||
{
|
||||
AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
|
||||
AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
||||
|
||||
if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
|
||||
{
|
||||
iq = PixelImap.Constant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
|
||||
@ -473,6 +484,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);
|
||||
|
||||
@ -511,7 +527,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
((isOutAttr && context.Config.LastInVertexPipeline) ||
|
||||
(!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
|
||||
{
|
||||
DeclareInputOrOutput(context, attr, (attr >> 2) & 3, isOutAttr, iq);
|
||||
DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -543,6 +559,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);
|
||||
|
||||
@ -562,7 +583,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
|
||||
{
|
||||
var tfOutput = context.GetTransformFeedbackOutput(attrInfo.BaseValue);
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
||||
@ -585,24 +606,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
|
||||
if (!isOutAttr)
|
||||
if (!isOutAttr &&
|
||||
!perPatch &&
|
||||
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
if (!perPatch &&
|
||||
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
|
||||
switch (iq)
|
||||
{
|
||||
case PixelImap.Constant:
|
||||
context.Decorate(spvVar, Decoration.Flat);
|
||||
break;
|
||||
case PixelImap.ScreenLinear:
|
||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
||||
break;
|
||||
}
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
}
|
||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
||||
@ -611,22 +620,52 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
|
||||
if (!isOutAttr)
|
||||
{
|
||||
switch (iq)
|
||||
{
|
||||
case PixelImap.Constant:
|
||||
context.Decorate(spvVar, Decoration.Flat);
|
||||
break;
|
||||
case PixelImap.ScreenLinear:
|
||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
context.AddGlobalVariable(spvVar);
|
||||
dict.Add(attrInfo.BaseValue, spvVar);
|
||||
}
|
||||
|
||||
private static void DeclareInputOrOutput(CodeGenContext context, int attr, int component, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
||||
private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
||||
{
|
||||
var dict = isOutAttr ? context.Outputs : context.Inputs;
|
||||
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
|
||||
|
||||
bool hasComponent = true;
|
||||
int component = (attr >> 2) & 3;
|
||||
int components = 1;
|
||||
var type = attrInfo.Type & AggregateType.ElementTypeMask;
|
||||
|
||||
if (context.Config.LastInPipeline && isOutAttr)
|
||||
{
|
||||
components = context.Info.GetTransformFeedbackOutputComponents(attr);
|
||||
|
||||
if (components > 1)
|
||||
{
|
||||
attr &= ~0xf;
|
||||
type = AggregateType.Vector | AggregateType.FP32;
|
||||
hasComponent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dict.ContainsKey(attr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||
var attrType = context.GetType(attrInfo.Type & AggregateType.ElementTypeMask);
|
||||
var attrType = context.GetType(type, components);
|
||||
|
||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
||||
{
|
||||
@ -634,6 +673,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);
|
||||
|
||||
@ -641,11 +685,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
|
||||
|
||||
if (hasComponent)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
|
||||
}
|
||||
|
||||
if (isOutAttr)
|
||||
{
|
||||
var tfOutput = context.GetTransformFeedbackOutput(location, component);
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
||||
@ -689,8 +737,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
|
||||
AttributeConsts.PointCoordX => BuiltIn.PointCoord,
|
||||
AttributeConsts.TessCoordX => BuiltIn.TessCoord,
|
||||
AttributeConsts.InstanceId => BuiltIn.InstanceId, // FIXME: Invalid
|
||||
AttributeConsts.VertexId => BuiltIn.VertexId, // FIXME: Invalid
|
||||
AttributeConsts.InstanceId => BuiltIn.InstanceId,
|
||||
AttributeConsts.VertexId => BuiltIn.VertexId,
|
||||
AttributeConsts.BaseInstance => BuiltIn.BaseInstance,
|
||||
AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
|
||||
AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
|
||||
AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
|
||||
AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
|
||||
AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
|
||||
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
|
||||
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
|
||||
|
@ -62,10 +62,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
context.AddCapability(Capability.TransformFeedback);
|
||||
}
|
||||
|
||||
if (config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock())
|
||||
if (config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
|
||||
context.AddExtension("SPV_EXT_fragment_shader_interlock");
|
||||
if (context.Info.Inputs.Contains(AttributeConsts.Layer))
|
||||
{
|
||||
context.AddCapability(Capability.Geometry);
|
||||
}
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock())
|
||||
{
|
||||
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
|
||||
context.AddExtension("SPV_EXT_fragment_shader_interlock");
|
||||
}
|
||||
}
|
||||
else if (config.Stage == ShaderStage.Geometry)
|
||||
{
|
||||
@ -81,6 +89,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
context.AddCapability(Capability.Tessellation);
|
||||
}
|
||||
else if (config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
context.AddCapability(Capability.DrawParameters);
|
||||
}
|
||||
|
||||
context.AddExtension("SPV_KHR_shader_ballot");
|
||||
context.AddExtension("SPV_KHR_subgroup_vote");
|
||||
|
@ -6,5 +6,9 @@ namespace Ryujinx.Graphics.Shader
|
||||
|
||||
public const int MaxAttributes = 16;
|
||||
public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes));
|
||||
|
||||
public const int NvnBaseVertexByteOffset = 0x640;
|
||||
public const int NvnBaseInstanceByteOffset = 0x644;
|
||||
public const int NvnDrawIndexByteOffset = 0x648;
|
||||
}
|
||||
}
|
@ -377,6 +377,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
|
||||
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
|
||||
{
|
||||
HashSet<ulong> visited = new HashSet<ulong>();
|
||||
|
||||
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
|
||||
ulong baseOffset = lastOp.GetAbsoluteAddress();
|
||||
|
||||
@ -392,9 +394,14 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
for (int i = 0; i < cbOffsetsCount; i++)
|
||||
{
|
||||
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
||||
Block target = getBlock(baseOffset + targetOffset);
|
||||
target.Predecessors.Add(block);
|
||||
block.Successors.Add(target);
|
||||
ulong targetAddress = baseOffset + targetOffset;
|
||||
|
||||
if (visited.Add(targetAddress))
|
||||
{
|
||||
Block target = getBlock(targetAddress);
|
||||
target.Predecessors.Add(block);
|
||||
block.Successors.Add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user