* [HLE] Let schedulers report guest thread stack bounds Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Core] Report the mapped stack of each guest thread Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Validate pthread stack bounds before reporting them Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Report every guest thread's stack in pthread attributes Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Publish semaphore handles as 64-bit values Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Accept zero-count semaphore signals Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Route semaphore waits through one core Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Poll host semaphore waits and deliver guest exceptions Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Add scePthreadSemTimedwait Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Remove every pending copy of a deleted event Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Block timed event queue waits in the guest scheduler Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Refresh pending user data when an event is re-added Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Keep every end-of-pipe interrupt event Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Track deadlines and POSIX results on sync waiters Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Poll host sync waits in bounded slices Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Share the sync-on-address wait and wake cores Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Kernel] Add _umtx_op wait and wake Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Np] Track WebApi2 user contexts and push filters Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Np] Add sceNpWebApi2PushEventDeleteHandle Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Np] Add sceNpWebApi2PushEventRegisterCallback Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Tests] Cover kernel sync, pthread stack bounds and NpWebApi2 Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Accept HTile-backed compressed stencil targets Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Tests] Cover HTile-backed compressed stencil targets Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Accept stencil targets with or without HTile backing * [Tests] Cover stencil compression with and without HTile * [ShaderCompiler] Treat V_CMPX as an EXEC write in every encoding * [Tests] Cover SDWA V_CMPX in the EXEC-full analysis * [GPU] Give images first found without metadata the DCC metadata of a texture request Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Tests] Cover DCC metadata adopted from a texture request Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Number command stream submissions in admission order Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Queue control barriers behind accepted command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Cancel pending control barriers when the command stream fails Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Cancel pending control barriers when the command stream is discarded Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Run relay work after accepted command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [VideoOut] Order relay work behind the presenter command stream Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [GPU] Let backends run work after pending command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [VideoOut] Run state changes after pending command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Metal] Run state changes after pending command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [VideoOut] Close ports after pending command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [VideoOut] Unregister buffers after pending command streams Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> * [Tests] Cover ordered video-out state changes Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> --------- Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com>
139 lines
4.2 KiB
C#
139 lines
4.2 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.ShaderCompiler;
|
|
using SharpEmu.ShaderCompiler.Ir;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.ShaderCompiler.Tests;
|
|
|
|
public sealed class Gen5ExecFullAnalysisTests
|
|
{
|
|
private const uint Exec = 126;
|
|
|
|
private readonly List<Gen5ShaderInstruction> _program = [];
|
|
private uint _pc;
|
|
|
|
private uint Add(string opcode, Gen5ShaderEncoding encoding, Gen5Operand[] destinations, Gen5Operand[] sources, uint word = 0)
|
|
{
|
|
var pc = _pc;
|
|
_program.Add(new Gen5ShaderInstruction(pc, encoding, opcode, [word], sources, destinations, null));
|
|
_pc += 4;
|
|
return pc;
|
|
}
|
|
|
|
private uint VectorAdd() =>
|
|
Add("VAddF32", Gen5ShaderEncoding.Vop2, [Gen5Operand.Vector(1)], [Gen5Operand.Vector(2), Gen5Operand.Vector(3)]);
|
|
|
|
private uint Scalar(string opcode, uint destination, params Gen5Operand[] sources) =>
|
|
Add(opcode, Gen5ShaderEncoding.Sop1, [Gen5Operand.Scalar(destination)], sources);
|
|
|
|
private uint Kill() =>
|
|
Add("VCmpxLtF32", Gen5ShaderEncoding.Vopc, [], [Gen5Operand.Vector(0), Gen5Operand.Vector(1)]);
|
|
|
|
private uint Branch(string opcode) => Add(opcode, Gen5ShaderEncoding.Sopp, [], [], 0xBF840000u);
|
|
|
|
private void PointBranch(uint branchPc, uint targetPc)
|
|
{
|
|
var index = _program.FindIndex(instruction => instruction.Pc == branchPc);
|
|
var original = _program[index];
|
|
var offset = (ushort)(short)((int)(targetPc - branchPc - 4) / 4);
|
|
_program[index] = original with { Words = [(original.Words[0] & 0xFFFF0000u) | offset] };
|
|
}
|
|
|
|
private IReadOnlySet<uint> Analyze() => Gen5ExecFullAnalysis.Analyze(new Gen5ShaderProgram(0x1000, _program), wave32: false);
|
|
|
|
[Fact]
|
|
public void StraightLineCode_RunsWithAFullExec()
|
|
{
|
|
var first = VectorAdd();
|
|
var second = VectorAdd();
|
|
|
|
var full = Analyze();
|
|
Assert.Contains(first, full);
|
|
Assert.Contains(second, full);
|
|
}
|
|
|
|
[Fact]
|
|
public void AfterAVectorCompareIntoExec_ExecIsUnknown()
|
|
{
|
|
var before = VectorAdd();
|
|
Kill();
|
|
var after = VectorAdd();
|
|
|
|
var full = Analyze();
|
|
Assert.Contains(before, full);
|
|
Assert.DoesNotContain(after, full);
|
|
}
|
|
|
|
[Fact]
|
|
public void AfterAnSdwaVectorCompareIntoExec_ExecIsUnknown()
|
|
{
|
|
var before = VectorAdd();
|
|
var pc = _pc;
|
|
_program.Add(new Gen5ShaderInstruction(
|
|
pc,
|
|
Gen5ShaderEncoding.Vopc,
|
|
"VCmpxNltF32",
|
|
[0u],
|
|
[Gen5Operand.Vector(0), Gen5Operand.Vector(1)],
|
|
[],
|
|
new Gen5SdwaControl(0, 0, 6, 6, false, false, 0, 0, 0, false, 106)));
|
|
_pc += 4;
|
|
var save = Scalar("SMovB64", 22, Gen5Operand.Scalar(Exec));
|
|
VectorAdd();
|
|
Scalar("SMovB64", Exec, Gen5Operand.Scalar(22));
|
|
var after = VectorAdd();
|
|
|
|
var full = Analyze();
|
|
Assert.Contains(before, full);
|
|
Assert.DoesNotContain(save, full);
|
|
Assert.DoesNotContain(after, full);
|
|
}
|
|
|
|
[Fact]
|
|
public void RestoringASavedFullExec_MakesItFullAgain()
|
|
{
|
|
Scalar("SMovB64", 40, Gen5Operand.Scalar(Exec));
|
|
Kill();
|
|
var masked = VectorAdd();
|
|
Scalar("SMovB64", Exec, Gen5Operand.Scalar(40));
|
|
var restored = VectorAdd();
|
|
|
|
var full = Analyze();
|
|
Assert.DoesNotContain(masked, full);
|
|
Assert.Contains(restored, full);
|
|
}
|
|
|
|
[Fact]
|
|
public void ACopyOverwrittenBeforeTheRestore_DoesNotCount()
|
|
{
|
|
Scalar("SMovB64", 40, Gen5Operand.Scalar(Exec));
|
|
Kill();
|
|
Scalar("SMovB32", 41, Gen5Operand.Scalar(3));
|
|
Scalar("SMovB64", Exec, Gen5Operand.Scalar(40));
|
|
var after = VectorAdd();
|
|
|
|
Assert.DoesNotContain(after, Analyze());
|
|
}
|
|
|
|
[Fact]
|
|
public void AJoinWithAPartialPath_IsUnknown()
|
|
{
|
|
var branch = Branch("SCbranchScc0");
|
|
Kill();
|
|
var join = VectorAdd();
|
|
PointBranch(branch, join);
|
|
|
|
Assert.DoesNotContain(join, Analyze());
|
|
}
|
|
|
|
[Fact]
|
|
public void WqmOfAFullExec_StaysFull()
|
|
{
|
|
Scalar("SWqmB64", Exec, Gen5Operand.Scalar(Exec));
|
|
var after = VectorAdd();
|
|
|
|
Assert.Contains(after, Analyze());
|
|
}
|
|
}
|