From 2064c1eda657963e9a1eb92bbabe43e01ee3619b Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Sat, 18 Jul 2026 23:02:12 +0300 Subject: [PATCH] [shader] add scalar memory fallback --- .../Agc/AgcShaderCompilerHooks.cs | 2 +- .../Gen5ShaderScalarEvaluator.cs | 3 +- .../Agc/Gen5ScalarMemoryFallbackTests.cs | 99 +++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 tests/SharpEmu.Libs.Tests/Agc/Gen5ScalarMemoryFallbackTests.cs diff --git a/src/SharpEmu.Libs/Agc/AgcShaderCompilerHooks.cs b/src/SharpEmu.Libs/Agc/AgcShaderCompilerHooks.cs index bb6699df..bb08b944 100644 --- a/src/SharpEmu.Libs/Agc/AgcShaderCompilerHooks.cs +++ b/src/SharpEmu.Libs/Agc/AgcShaderCompilerHooks.cs @@ -27,7 +27,7 @@ internal static class AgcShaderCompilerHooks internal static void Install() { Gen5ShaderScalarEvaluator.FallbackMemoryReader = - KernelMemoryCompatExports.TryReadTrackedLibcHeap; + KernelMemoryCompatExports.TryReadShaderGuestMemory; Gen5ShaderScalarEvaluator.GlobalMemoryPool = GuestDataPool.Shared; } diff --git a/src/SharpEmu.ShaderCompiler/Gen5ShaderScalarEvaluator.cs b/src/SharpEmu.ShaderCompiler/Gen5ShaderScalarEvaluator.cs index 0dc2d257..7a8d21f4 100644 --- a/src/SharpEmu.ShaderCompiler/Gen5ShaderScalarEvaluator.cs +++ b/src/SharpEmu.ShaderCompiler/Gen5ShaderScalarEvaluator.cs @@ -2350,7 +2350,8 @@ public static class Gen5ShaderScalarEvaluator private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value) { Span bytes = stackalloc byte[sizeof(uint)]; - if (!ctx.Memory.TryRead(address, bytes)) + if (!ctx.Memory.TryRead(address, bytes) && + FallbackMemoryReader?.Invoke(address, bytes) != true) { value = 0; return false; diff --git a/tests/SharpEmu.Libs.Tests/Agc/Gen5ScalarMemoryFallbackTests.cs b/tests/SharpEmu.Libs.Tests/Agc/Gen5ScalarMemoryFallbackTests.cs new file mode 100644 index 00000000..63a43eb1 --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Agc/Gen5ScalarMemoryFallbackTests.cs @@ -0,0 +1,99 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using System.Buffers.Binary; +using SharpEmu.HLE; +using SharpEmu.ShaderCompiler; +using Xunit; + +namespace SharpEmu.Libs.Tests.Agc; + +public sealed class Gen5ScalarMemoryFallbackTests +{ + private const ulong ScalarTableAddress = 0x4_4665_4FD0; + private static readonly object FallbackReaderGate = new(); + + [Fact] + public void ScalarLoadReadsTrackedFallbackMemory() + { + var expected = new uint[] + { + 0x4665_4F70, + 0x0000_0004, + 0x4EA7_FCE0, + 0x0000_0004, + }; + var table = new byte[expected.Length * sizeof(uint)]; + for (var index = 0; index < expected.Length; index++) + { + BinaryPrimitives.WriteUInt32LittleEndian( + table.AsSpan(index * sizeof(uint), sizeof(uint)), + expected[index]); + } + + var load = new Gen5ShaderInstruction( + 0, + Gen5ShaderEncoding.Smem, + "SLoadDwordx4", + [], + [Gen5Operand.Scalar(0)], + [ + Gen5Operand.Scalar(16), + Gen5Operand.Scalar(17), + Gen5Operand.Scalar(18), + Gen5Operand.Scalar(19), + ], + new Gen5ScalarMemoryControl(4, 0, null)); + var end = new Gen5ShaderInstruction( + 8, + Gen5ShaderEncoding.Sopp, + "SEndpgm", + [], + [], + [], + null); + var state = new Gen5ShaderState( + new Gen5ShaderProgram(0, [load, end]), + [unchecked((uint)ScalarTableAddress), (uint)(ScalarTableAddress >> 32)], + null); + var ctx = new CpuContext(new FakeCpuMemory(0x1000, 0x100), Generation.Gen5); + + lock (FallbackReaderGate) + { + var previousReader = Gen5ShaderScalarEvaluator.FallbackMemoryReader; + try + { + Gen5ShaderScalarEvaluator.FallbackMemoryReader = ReadFallback; + Assert.True( + Gen5ShaderScalarEvaluator.TryEvaluate( + ctx, + state, + out var evaluation, + out var error), + error); + Assert.Equal(expected, evaluation.ScalarRegisters.Skip(16).Take(4)); + } + finally + { + Gen5ShaderScalarEvaluator.FallbackMemoryReader = previousReader; + } + } + + bool ReadFallback(ulong address, Span destination) + { + if (address < ScalarTableAddress) + { + return false; + } + + var offset = address - ScalarTableAddress; + if (offset + (ulong)destination.Length > (ulong)table.Length) + { + return false; + } + + table.AsSpan((int)offset, destination.Length).CopyTo(destination); + return true; + } + } +}