fix(gpu): support Gen5 flat memory and 3D images (#587)

Vector-mesh UI text samples type-10 volume LUTs; treat MIMG DIM=2 as
Dim3D and transport depth through AGC and Vulkan so Z slices no longer
collapse into a single 2D plane.
This commit is contained in:
MarcelMediaDev
2026-07-24 13:44:58 +01:00
committed by GitHub
parent 21f964a0dc
commit 5228335f15
14 changed files with 1311 additions and 299 deletions
@@ -314,7 +314,8 @@ public static partial class Gen5SpirvTranslator
uint VectorType,
ImageComponentKind ComponentKind,
bool IsStorage,
bool Arrayed);
bool Arrayed,
SpirvImageDim Dimension);
private readonly record struct SpirvVertexInput(
uint Variable,
@@ -1001,11 +1002,15 @@ public static partial class Gen5SpirvTranslator
SpirvCapability.StorageImageExtendedFormats);
}
var isArrayed = !isStorage &&
var dimension = binding.Control.Dimension == 2
? SpirvImageDim.Dim3D
: SpirvImageDim.Dim2D;
var isArrayed = dimension != SpirvImageDim.Dim3D &&
!isStorage &&
Gen5ShaderTranslator.IsArrayedImageBinding(binding);
var imageType = _module.TypeImage(
componentType,
SpirvImageDim.Dim2D,
dimension,
depth: false,
arrayed: isArrayed,
multisampled: false,
@@ -1035,7 +1040,8 @@ public static partial class Gen5SpirvTranslator
_module.TypeVector(componentType, 4),
componentKind,
isStorage,
isArrayed));
isArrayed,
dimension));
_interfaces.Add(variable);
}
}
@@ -2259,20 +2265,39 @@ public static partial class Gen5SpirvTranslator
return false;
}
var memoryOpcode = control.UsesFlatAddress
? "Global" + instruction.Opcode["Flat".Length..]
: instruction.Opcode;
var vectorByteAddress = LoadV(control.VectorAddress);
if (control.UsesFlatAddress)
{
// FLAT instructions carry the complete 64-bit guest address
// in a VGPR pair. The scalar evaluator captures the buffer
// rooted at the inferred SGPR pair, so convert the low address
// dword back to a byte offset inside that binding. Subtraction
// modulo 2^32 also handles an address addition that carried
// into the high dword, while the bounded binding prevents an
// unrelated pointer from escaping into host storage.
vectorByteAddress = _module.AddInstruction(
SpirvOp.ISub,
_uintType,
vectorByteAddress,
LoadS(control.ScalarAddress));
}
var byteAddress = IAdd(
LoadV(control.VectorAddress),
vectorByteAddress,
UInt(unchecked((uint)control.OffsetBytes)));
byteAddress = ApplyGuestBufferByteBias(bindingIndex, byteAddress);
var dwordAddress = ShiftRightLogical(byteAddress, UInt(2));
if (instruction.Opcode is "GlobalAtomicAdd" or "GlobalAtomicUMax")
if (memoryOpcode is "GlobalAtomicAdd" or "GlobalAtomicUMax")
{
EmitExecConditional(() =>
{
EmitConditional(IsBufferWordInRange(bindingIndex, dwordAddress), () =>
{
var original = _module.AddInstruction(
instruction.Opcode == "GlobalAtomicAdd"
memoryOpcode == "GlobalAtomicAdd"
? SpirvOp.AtomicIAdd
: SpirvOp.AtomicUMax,
_uintType,
@@ -2289,12 +2314,12 @@ public static partial class Gen5SpirvTranslator
return true;
}
if (instruction.Opcode.StartsWith("GlobalStore", StringComparison.Ordinal))
if (memoryOpcode.StartsWith("GlobalStore", StringComparison.Ordinal))
{
EmitExecConditional(() =>
{
if (TryGetSubdwordStoreInfo(
instruction.Opcode,
memoryOpcode,
out var byteCount,
out var sourceShift))
{
@@ -2324,7 +2349,7 @@ public static partial class Gen5SpirvTranslator
}
if (TryGetSubdwordLoadInfo(
instruction.Opcode,
memoryOpcode,
out var loadByteCount,
out var signExtend,
out var d16,
@@ -3268,6 +3293,7 @@ public static partial class Gen5SpirvTranslator
var imageObject = Load(resource.ObjectType, resource.Variable);
if (instruction.Opcode == "ImageGetResinfo")
{
var sizeComponentCount = ImageCoordinateComponentCount(resource);
var queryImage = resource.IsStorage
? imageObject
: _module.AddInstruction(
@@ -3278,7 +3304,7 @@ public static partial class Gen5SpirvTranslator
resource.IsStorage
? SpirvOp.ImageQuerySize
: SpirvOp.ImageQuerySizeLod,
_module.TypeVector(_intType, 2),
_module.TypeVector(_intType, sizeComponentCount),
resource.IsStorage
? [queryImage]
: [queryImage, UInt(0)]);
@@ -3291,7 +3317,7 @@ public static partial class Gen5SpirvTranslator
}
uint value;
if (component < 2)
if (component < sizeComponentCount)
{
var signedValue = _module.AddInstruction(
SpirvOp.CompositeExtract,
@@ -3319,7 +3345,12 @@ public static partial class Gen5SpirvTranslator
return false;
}
var coordinates = BuildIntegerCoordinates(image, 0);
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var coordinates = BuildIntegerCoordinates(
image,
0,
coordinateComponentCount);
var components = new uint[4];
uint sourceIndex = 0;
for (var component = 0; component < components.Length; component++)
@@ -3355,13 +3386,14 @@ public static partial class Gen5SpirvTranslator
components);
var imageSize = _module.AddInstruction(
SpirvOp.ImageQuerySize,
_module.TypeVector(_intType, 2),
_module.TypeVector(_intType, coordinateComponentCount),
imageObject);
EmitBoundsCheckedImageWrite(
coordinates,
imageSize,
imageObject,
texel);
texel,
coordinateComponentCount);
return true;
}
@@ -3382,14 +3414,17 @@ public static partial class Gen5SpirvTranslator
}
var signed = resource.ComponentKind == ImageComponentKind.Sint;
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var atomicImageSize = _module.AddInstruction(
SpirvOp.ImageQuerySize,
_module.TypeVector(_intType, 2),
_module.TypeVector(_intType, coordinateComponentCount),
imageObject);
var coordinates = BuildClampedIntegerCoordinates(
image,
0,
atomicImageSize);
atomicImageSize,
coordinateComponentCount);
EmitExecConditional(() =>
{
var pointer = _module.AddInstruction(
@@ -3433,14 +3468,17 @@ public static partial class Gen5SpirvTranslator
{
if (resource.IsStorage)
{
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var imageSize = _module.AddInstruction(
SpirvOp.ImageQuerySize,
_module.TypeVector(_intType, 2),
_module.TypeVector(_intType, coordinateComponentCount),
imageObject);
var coordinates = BuildClampedIntegerCoordinates(
image,
0,
imageSize);
imageSize,
coordinateComponentCount);
sampled = _module.AddInstruction(
SpirvOp.ImageRead,
resource.VectorType,
@@ -3454,15 +3492,18 @@ public static partial class Gen5SpirvTranslator
SpirvOp.Image,
resource.ImageType,
imageObject);
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var imageSize = _module.AddInstruction(
SpirvOp.ImageQuerySizeLod,
_module.TypeVector(_intType, 2),
_module.TypeVector(_intType, coordinateComponentCount),
fetchedImage,
UInt(mipLevel));
var coordinates = BuildClampedIntegerCoordinates(
image,
0,
imageSize);
imageSize,
coordinateComponentCount);
sampled = _module.AddInstruction(
SpirvOp.ImageFetch,
resource.VectorType,
@@ -3494,12 +3535,19 @@ public static partial class Gen5SpirvTranslator
// lowering treated SAMPLE_D as body-first and consequently
// sampled gradients as coordinates in every captured
// derivative operation.
var spatialComponentCount =
ImageSpatialComponentCount(resource);
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var addressCursor = 0;
var offset = 0u;
if (hasOffset)
{
addressCursor = AlignFullImageAddress(image, addressCursor);
offset = BuildImageOffset(image, addressCursor);
offset = BuildImageOffset(
image,
addressCursor,
spatialComponentCount);
addressCursor += ImageFullAddressSlots(image);
}
@@ -3523,26 +3571,33 @@ public static partial class Gen5SpirvTranslator
}
var gradientX = hasGradients
? BuildFloatCoordinates(image, addressCursor)
? BuildFloatCoordinates(
image,
addressCursor,
spatialComponentCount)
: 0u;
var gradientY = hasGradients
? BuildFloatCoordinates(image, addressCursor + 2)
? BuildFloatCoordinates(
image,
addressCursor + (int)spatialComponentCount,
spatialComponentCount)
: 0u;
if (hasGradients)
{
addressCursor += 4;
addressCursor += checked((int)(spatialComponentCount * 2));
}
var coordinates = resource.Arrayed
? BuildFloatArrayCoordinates(image, addressCursor)
: BuildFloatCoordinates(image, addressCursor);
var coordinates = BuildFloatCoordinates(
image,
addressCursor,
coordinateComponentCount);
var explicitLod = hasGradients || hasZeroLod || hasLod;
var lod = hasZeroLod
? Float(0)
: hasLod
? LoadImageFloatAddress(
image,
addressCursor + (resource.Arrayed ? 3 : 2))
addressCursor + (int)coordinateComponentCount)
: lodOrBias;
if (hasOffset)
{
@@ -3608,11 +3663,18 @@ public static partial class Gen5SpirvTranslator
instruction.Opcode.EndsWith("O", StringComparison.Ordinal);
var hasCompare =
instruction.Opcode.Contains("Gather4C", StringComparison.Ordinal);
var spatialComponentCount =
ImageSpatialComponentCount(resource);
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var addressCursor = 0;
var offset = 0u;
if (hasOffset)
{
offset = BuildImageOffset(image, addressCursor);
offset = BuildImageOffset(
image,
addressCursor,
spatialComponentCount);
addressCursor += ImageFullAddressSlots(image);
}
@@ -3627,9 +3689,10 @@ public static partial class Gen5SpirvTranslator
addressCursor += ImageFullAddressSlots(image);
}
var coordinates = resource.Arrayed
? BuildFloatArrayCoordinates(image, addressCursor)
: BuildFloatCoordinates(image, addressCursor);
var coordinates = BuildFloatCoordinates(
image,
addressCursor,
coordinateComponentCount);
var operands = new List<uint>
{
imageObject,
@@ -3822,28 +3885,31 @@ public static partial class Gen5SpirvTranslator
});
}
private uint BuildFloatCoordinates(Gen5ImageControl image, int start)
{
var x = LoadImageFloatAddress(image, start);
var y = LoadImageFloatAddress(image, start + 1);
return _module.AddInstruction(
SpirvOp.CompositeConstruct,
_vec2Type,
x,
y);
}
private static uint ImageSpatialComponentCount(
SpirvImageResource resource) =>
resource.Dimension == SpirvImageDim.Dim3D ? 3u : 2u;
private uint BuildFloatArrayCoordinates(Gen5ImageControl image, int start)
private static uint ImageCoordinateComponentCount(
SpirvImageResource resource) =>
resource.Arrayed ? 3u : ImageSpatialComponentCount(resource);
private uint BuildFloatCoordinates(
Gen5ImageControl image,
int start,
uint componentCount)
{
var x = LoadImageFloatAddress(image, start);
var y = LoadImageFloatAddress(image, start + 1);
var slice = LoadImageFloatAddress(image, start + 2);
var components = new uint[checked((int)componentCount)];
for (var component = 0; component < components.Length; component++)
{
components[component] = LoadImageFloatAddress(
image,
start + component);
}
return _module.AddInstruction(
SpirvOp.CompositeConstruct,
_vec3Type,
x,
y,
slice);
_module.TypeVector(_floatType, componentCount),
components);
}
private static int ImageAddressRegister(
@@ -3948,47 +4014,49 @@ public static partial class Gen5SpirvTranslator
ShiftLeftLogical(BitwiseAnd(high, UInt(0xFFFF)), UInt(16)));
}
private uint BuildIntegerCoordinates(Gen5ImageControl image, int start)
private uint BuildIntegerCoordinates(
Gen5ImageControl image,
int start,
uint componentCount)
{
var ivec2 = _module.TypeVector(_intType, 2);
var x = Bitcast(_intType, LoadImageIntegerAddress(image, start));
var y = Bitcast(_intType, LoadImageIntegerAddress(image, start + 1));
var components = new uint[checked((int)componentCount)];
for (var component = 0; component < components.Length; component++)
{
components[component] = Bitcast(
_intType,
LoadImageIntegerAddress(image, start + component));
}
return _module.AddInstruction(
SpirvOp.CompositeConstruct,
ivec2,
x,
y);
_module.TypeVector(_intType, componentCount),
components);
}
private uint BuildClampedIntegerCoordinates(
Gen5ImageControl image,
int start,
uint imageSize)
uint imageSize,
uint componentCount)
{
var ivec2 = _module.TypeVector(_intType, 2);
var x = ClampSignedCoordinate(
Bitcast(
_intType,
LoadImageIntegerAddress(image, start)),
_module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
imageSize,
0));
var y = ClampSignedCoordinate(
Bitcast(
_intType,
LoadImageIntegerAddress(image, start + 1)),
_module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
imageSize,
1));
var components = new uint[checked((int)componentCount)];
for (var component = 0; component < components.Length; component++)
{
components[component] = ClampSignedCoordinate(
Bitcast(
_intType,
LoadImageIntegerAddress(image, start + component)),
_module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
imageSize,
(uint)component));
}
return _module.AddInstruction(
SpirvOp.CompositeConstruct,
ivec2,
x,
y);
_module.TypeVector(_intType, componentCount),
components);
}
private uint ClampSignedCoordinate(uint value, uint extent)
@@ -4027,69 +4095,47 @@ public static partial class Gen5SpirvTranslator
uint coordinates,
uint imageSize,
uint imageObject,
uint texel)
uint texel,
uint coordinateComponentCount)
{
var x = _module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
coordinates,
0);
var y = _module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
coordinates,
1);
var width = _module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
imageSize,
0);
var height = _module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
imageSize,
1);
var zero = _module.Constant(_intType, 0);
var xNonNegative = _module.AddInstruction(
SpirvOp.SGreaterThanEqual,
_boolType,
x,
zero);
var yNonNegative = _module.AddInstruction(
SpirvOp.SGreaterThanEqual,
_boolType,
y,
zero);
var xInRange = _module.AddInstruction(
SpirvOp.SLessThan,
_boolType,
x,
width);
var yInRange = _module.AddInstruction(
SpirvOp.SLessThan,
_boolType,
y,
height);
var lowerInRange = _module.AddInstruction(
SpirvOp.LogicalAnd,
_boolType,
xNonNegative,
yNonNegative);
var upperInRange = _module.AddInstruction(
SpirvOp.LogicalAnd,
_boolType,
xInRange,
yInRange);
var inRange = _module.AddInstruction(
SpirvOp.LogicalAnd,
_boolType,
lowerInRange,
upperInRange);
inRange = _module.AddInstruction(
SpirvOp.LogicalAnd,
_boolType,
Load(_boolType, _exec),
inRange);
var inRange = Load(_boolType, _exec);
for (uint component = 0;
component < coordinateComponentCount;
component++)
{
var coordinate = _module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
coordinates,
component);
var extent = _module.AddInstruction(
SpirvOp.CompositeExtract,
_intType,
imageSize,
component);
var nonNegative = _module.AddInstruction(
SpirvOp.SGreaterThanEqual,
_boolType,
coordinate,
zero);
var belowExtent = _module.AddInstruction(
SpirvOp.SLessThan,
_boolType,
coordinate,
extent);
var componentInRange = _module.AddInstruction(
SpirvOp.LogicalAnd,
_boolType,
nonNegative,
belowExtent);
inRange = _module.AddInstruction(
SpirvOp.LogicalAnd,
_boolType,
inRange,
componentInRange);
}
var writeLabel = _module.AllocateId();
var mergeLabel = _module.AllocateId();
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
@@ -4108,30 +4154,30 @@ public static partial class Gen5SpirvTranslator
_module.AddLabel(mergeLabel);
}
private uint BuildImageOffset(Gen5ImageControl image, int component)
private uint BuildImageOffset(
Gen5ImageControl image,
int component,
uint componentCount)
{
var ivec2 = _module.TypeVector(_intType, 2);
var packed = Bitcast(
_intType,
LoadV(image.GetAddressRegister(
ImageAddressRegister(image, component))));
var x = _module.AddInstruction(
SpirvOp.BitFieldSExtract,
_intType,
packed,
UInt(0),
UInt(6));
var y = _module.AddInstruction(
SpirvOp.BitFieldSExtract,
_intType,
packed,
UInt(8),
UInt(6));
var components = new uint[checked((int)componentCount)];
for (var index = 0; index < components.Length; index++)
{
components[index] = _module.AddInstruction(
SpirvOp.BitFieldSExtract,
_intType,
packed,
UInt((uint)(index * 8)),
UInt(6));
}
return _module.AddInstruction(
SpirvOp.CompositeConstruct,
ivec2,
x,
y);
_module.TypeVector(_intType, componentCount),
components);
}
private uint ApplyDynamicSampleOffset(
@@ -4141,7 +4187,16 @@ public static partial class Gen5SpirvTranslator
uint texelOffset,
uint lod)
{
var ivec2 = _module.TypeVector(_intType, 2);
var spatialComponentCount = ImageSpatialComponentCount(resource);
var coordinateComponentCount =
ImageCoordinateComponentCount(resource);
var spatialIntegerType =
_module.TypeVector(_intType, spatialComponentCount);
var spatialFloatType =
_module.TypeVector(_floatType, spatialComponentCount);
var queryComponentCount = resource.Arrayed
? coordinateComponentCount
: spatialComponentCount;
var image = _module.AddInstruction(
SpirvOp.Image,
resource.ImageType,
@@ -4163,14 +4218,14 @@ public static partial class Gen5SpirvTranslator
signedLod);
var size = _module.AddInstruction(
SpirvOp.ImageQuerySizeLod,
resource.Arrayed ? _module.TypeVector(_intType, 3) : ivec2,
_module.TypeVector(_intType, queryComponentCount),
image,
clampedLod);
if (resource.Arrayed)
{
size = _module.AddInstruction(
SpirvOp.VectorShuffle,
ivec2,
spatialIntegerType,
size,
size,
0u,
@@ -4179,27 +4234,27 @@ public static partial class Gen5SpirvTranslator
var sizeFloat = _module.AddInstruction(
SpirvOp.ConvertSToF,
_vec2Type,
spatialFloatType,
size);
var offsetFloat = _module.AddInstruction(
SpirvOp.ConvertSToF,
_vec2Type,
spatialFloatType,
texelOffset);
var normalizedOffset = _module.AddInstruction(
SpirvOp.FDiv,
_vec2Type,
spatialFloatType,
offsetFloat,
sizeFloat);
if (!resource.Arrayed)
{
return _module.AddInstruction(
SpirvOp.FAdd,
_vec2Type,
spatialFloatType,
coordinates,
normalizedOffset);
}
var offsetVec3 = _module.AddInstruction(
var arrayOffset = _module.AddInstruction(
SpirvOp.CompositeConstruct,
_vec3Type,
_module.AddInstruction(
@@ -4217,7 +4272,7 @@ public static partial class Gen5SpirvTranslator
SpirvOp.FAdd,
_vec3Type,
coordinates,
offsetVec3);
arrayOffset);
}
private bool TryEmitExport(