mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-26 04:39:17 +08:00
a4748ce266
* [shader-decoder-part1] Implemented a shader decoder for Gen5 shaders, including IR generation, metadata reading, scalar evaluation, and SPIR-V translation. Updated related exports and video output components to support the new shader decoding functionality. * [shader decoder] correct RDNA2 operands, fixing synchronization problems * [shader-decoder] RDNA2 decoder improvements * [shader-decoder] fix RDNA2 shift masking and sprite draws * [shader-decoder] improve RDNA2 shader decoder to support more instructions and fix some issues with the previous implementation.
155 lines
4.7 KiB
C#
155 lines
4.7 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.HLE;
|
|
using System.Buffers.Binary;
|
|
|
|
namespace SharpEmu.Libs.Agc;
|
|
|
|
internal static class Gen5ShaderMetadataReader
|
|
{
|
|
private const ulong ShaderUserDataOffset = 0x08;
|
|
private const int ResourceClassCount = 4;
|
|
private const int MaxMetadataEntries = 4096;
|
|
|
|
public static bool TryRead(
|
|
CpuContext ctx,
|
|
ulong shaderHeaderAddress,
|
|
out Gen5ShaderMetadata metadata)
|
|
{
|
|
metadata = default!;
|
|
if (!TryReadUInt64(ctx, shaderHeaderAddress + ShaderUserDataOffset, out var userDataAddress) ||
|
|
userDataAddress == 0 ||
|
|
!TryReadUInt64(ctx, userDataAddress, out var directResourceOffsetsAddress))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var resourceOffsets = new ulong[ResourceClassCount];
|
|
for (var resourceClass = 0; resourceClass < ResourceClassCount; resourceClass++)
|
|
{
|
|
if (!TryReadUInt64(
|
|
ctx,
|
|
userDataAddress + 0x08 + (ulong)(resourceClass * sizeof(ulong)),
|
|
out resourceOffsets[resourceClass]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!TryReadUInt16(ctx, userDataAddress + 0x28, out var extendedUserDataSize) ||
|
|
!TryReadUInt16(ctx, userDataAddress + 0x2A, out var shaderResourceTableSize) ||
|
|
!TryReadUInt16(ctx, userDataAddress + 0x2C, out var directResourceCount) ||
|
|
directResourceCount > MaxMetadataEntries)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var resourceCounts = new ushort[ResourceClassCount];
|
|
for (var resourceClass = 0; resourceClass < ResourceClassCount; resourceClass++)
|
|
{
|
|
if (!TryReadUInt16(
|
|
ctx,
|
|
userDataAddress + 0x2E + (ulong)(resourceClass * sizeof(ushort)),
|
|
out resourceCounts[resourceClass]) ||
|
|
resourceCounts[resourceClass] > MaxMetadataEntries)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var directResources = new Dictionary<uint, uint>();
|
|
if (directResourceCount != 0)
|
|
{
|
|
if (directResourceOffsetsAddress == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (uint type = 0; type < directResourceCount; type++)
|
|
{
|
|
if (!TryReadUInt16(ctx, directResourceOffsetsAddress + type * sizeof(ushort), out var offset))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (offset != ushort.MaxValue)
|
|
{
|
|
directResources[type] = offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
var resources = new List<Gen5ShaderResourceMapping>();
|
|
for (var resourceClass = 0; resourceClass < ResourceClassCount; resourceClass++)
|
|
{
|
|
var count = resourceCounts[resourceClass];
|
|
if (count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (resourceOffsets[resourceClass] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (uint slot = 0; slot < count; slot++)
|
|
{
|
|
if (!TryReadUInt16(
|
|
ctx,
|
|
resourceOffsets[resourceClass] + slot * sizeof(ushort),
|
|
out var sharp))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var offset = (uint)(sharp & 0x7FFF);
|
|
if (offset == 0x7FFF)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
resources.Add(new Gen5ShaderResourceMapping(
|
|
(Gen5ShaderResourceKind)resourceClass,
|
|
slot,
|
|
offset,
|
|
(sharp & 0x8000) != 0));
|
|
}
|
|
}
|
|
|
|
metadata = new Gen5ShaderMetadata(
|
|
extendedUserDataSize,
|
|
shaderResourceTableSize,
|
|
directResources,
|
|
resources);
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadUInt16(CpuContext ctx, ulong address, out ushort value)
|
|
{
|
|
Span<byte> bytes = stackalloc byte[sizeof(ushort)];
|
|
if (!ctx.Memory.TryRead(address, bytes))
|
|
{
|
|
value = 0;
|
|
return false;
|
|
}
|
|
|
|
value = BinaryPrimitives.ReadUInt16LittleEndian(bytes);
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
|
|
{
|
|
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
|
|
if (!ctx.Memory.TryRead(address, bytes))
|
|
{
|
|
value = 0;
|
|
return false;
|
|
}
|
|
|
|
value = BinaryPrimitives.ReadUInt64LittleEndian(bytes);
|
|
return true;
|
|
}
|
|
}
|