mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-01 07:29:43 +08:00
652 lines
30 KiB
C#
652 lines
30 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
namespace SharpEmu.ShaderCompiler.Vulkan;
|
|
|
|
public static class SpirvFixedShaders
|
|
{
|
|
public static byte[] CreateFullscreenVertex(uint attributeCount)
|
|
{
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
|
|
var voidType = module.TypeVoid();
|
|
var boolType = module.TypeBool();
|
|
var uintType = module.TypeInt(32, signed: false);
|
|
var floatType = module.TypeFloat(32);
|
|
var vec4Type = module.TypeVector(floatType, 4);
|
|
var inputUintPointer = module.TypePointer(SpirvStorageClass.Input, uintType);
|
|
var outputVec4Pointer = module.TypePointer(SpirvStorageClass.Output, vec4Type);
|
|
|
|
var vertexIndex = module.AddGlobalVariable(inputUintPointer, SpirvStorageClass.Input);
|
|
module.AddName(vertexIndex, "vertexIndex");
|
|
module.AddDecoration(
|
|
vertexIndex,
|
|
SpirvDecoration.BuiltIn,
|
|
(uint)SpirvBuiltIn.VertexIndex);
|
|
|
|
var position = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output);
|
|
module.AddName(position, "position");
|
|
module.AddDecoration(position, SpirvDecoration.BuiltIn, (uint)SpirvBuiltIn.Position);
|
|
|
|
var attributes = new uint[attributeCount];
|
|
for (uint index = 0; index < attributeCount; index++)
|
|
{
|
|
attributes[index] =
|
|
module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output);
|
|
module.AddName(attributes[index], $"attr{index}");
|
|
module.AddDecoration(attributes[index], SpirvDecoration.Location, index);
|
|
module.AddDecoration(attributes[index], SpirvDecoration.NoPerspective);
|
|
}
|
|
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddName(main, "main");
|
|
module.AddLabel();
|
|
|
|
var indexValue = module.AddInstruction(SpirvOp.Load, uintType, vertexIndex);
|
|
var one = module.Constant(uintType, 1);
|
|
var two = module.Constant(uintType, 2);
|
|
var shifted = module.AddInstruction(SpirvOp.ShiftLeftLogical, uintType, indexValue, one);
|
|
var xBits = module.AddInstruction(SpirvOp.BitwiseAnd, uintType, shifted, two);
|
|
var yBits = module.AddInstruction(SpirvOp.BitwiseAnd, uintType, indexValue, two);
|
|
var x = module.AddInstruction(SpirvOp.ConvertUToF, floatType, xBits);
|
|
var y = module.AddInstruction(SpirvOp.ConvertUToF, floatType, yBits);
|
|
var zero = module.ConstantFloat(floatType, 0f);
|
|
var oneFloat = module.ConstantFloat(floatType, 1f);
|
|
var twoFloat = module.ConstantFloat(floatType, 2f);
|
|
var xPosition = module.AddInstruction(SpirvOp.FMul, floatType, x, twoFloat);
|
|
xPosition = module.AddInstruction(SpirvOp.FSub, floatType, xPosition, oneFloat);
|
|
var yPosition = module.AddInstruction(SpirvOp.FMul, floatType, y, twoFloat);
|
|
yPosition = module.AddInstruction(SpirvOp.FSub, floatType, yPosition, oneFloat);
|
|
var positionValue = module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
vec4Type,
|
|
xPosition,
|
|
yPosition,
|
|
zero,
|
|
oneFloat);
|
|
module.AddStatement(SpirvOp.Store, position, positionValue);
|
|
|
|
var attributeValue = module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
vec4Type,
|
|
x,
|
|
y,
|
|
zero,
|
|
oneFloat);
|
|
foreach (var attribute in attributes)
|
|
{
|
|
module.AddStatement(SpirvOp.Store, attribute, attributeValue);
|
|
}
|
|
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
|
|
var interfaces = new uint[2 + attributes.Length];
|
|
interfaces[0] = vertexIndex;
|
|
interfaces[1] = position;
|
|
attributes.CopyTo(interfaces, 2);
|
|
module.AddEntryPoint(SpirvExecutionModel.Vertex, main, "main", interfaces);
|
|
_ = boolType;
|
|
return module.Build();
|
|
}
|
|
|
|
public static byte[] CreateCopyFragment(float colorScale = 1f)
|
|
{
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
|
|
var voidType = module.TypeVoid();
|
|
var floatType = module.TypeFloat(32);
|
|
var vec2Type = module.TypeVector(floatType, 2);
|
|
var vec4Type = module.TypeVector(floatType, 4);
|
|
var inputVec4Pointer = module.TypePointer(SpirvStorageClass.Input, vec4Type);
|
|
var outputVec4Pointer = module.TypePointer(SpirvStorageClass.Output, vec4Type);
|
|
var imageType = module.TypeImage(
|
|
floatType,
|
|
SpirvImageDim.Dim2D,
|
|
depth: false,
|
|
arrayed: false,
|
|
multisampled: false,
|
|
sampled: 1,
|
|
SpirvImageFormat.Unknown);
|
|
var sampledImageType = module.TypeSampledImage(imageType);
|
|
var sampledImagePointer =
|
|
module.TypePointer(SpirvStorageClass.UniformConstant, sampledImageType);
|
|
|
|
var attribute = module.AddGlobalVariable(inputVec4Pointer, SpirvStorageClass.Input);
|
|
module.AddName(attribute, "attr0");
|
|
module.AddDecoration(attribute, SpirvDecoration.Location, 0);
|
|
|
|
var texture = module.AddGlobalVariable(
|
|
sampledImagePointer,
|
|
SpirvStorageClass.UniformConstant);
|
|
module.AddName(texture, "tex0");
|
|
module.AddDecoration(texture, SpirvDecoration.DescriptorSet, 0);
|
|
module.AddDecoration(texture, SpirvDecoration.Binding, 1);
|
|
|
|
var output = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output);
|
|
module.AddName(output, "outColor");
|
|
module.AddDecoration(output, SpirvDecoration.Location, 0);
|
|
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddName(main, "main");
|
|
module.AddLabel();
|
|
|
|
var attributeValue = module.AddInstruction(SpirvOp.Load, vec4Type, attribute);
|
|
var coordinates = module.AddInstruction(
|
|
SpirvOp.VectorShuffle,
|
|
vec2Type,
|
|
attributeValue,
|
|
attributeValue,
|
|
0,
|
|
1);
|
|
var sampledImage = module.AddInstruction(SpirvOp.Load, sampledImageType, texture);
|
|
var lod = module.ConstantFloat(floatType, 0f);
|
|
var color = module.AddInstruction(
|
|
SpirvOp.ImageSampleExplicitLod,
|
|
vec4Type,
|
|
sampledImage,
|
|
coordinates,
|
|
2,
|
|
lod);
|
|
if (colorScale != 1f)
|
|
{
|
|
var scale = module.ConstantComposite(
|
|
vec4Type,
|
|
module.ConstantFloat(floatType, colorScale),
|
|
module.ConstantFloat(floatType, colorScale),
|
|
module.ConstantFloat(floatType, colorScale),
|
|
module.ConstantFloat(floatType, 1f));
|
|
color = module.AddInstruction(SpirvOp.FMul, vec4Type, color, scale);
|
|
}
|
|
module.AddStatement(SpirvOp.Store, output, color);
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
|
|
module.AddEntryPoint(
|
|
SpirvExecutionModel.Fragment,
|
|
main,
|
|
"main",
|
|
[attribute, texture, output]);
|
|
module.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft);
|
|
return module.Build();
|
|
}
|
|
|
|
public static byte[] CreatePqToScRgbFragment()
|
|
{
|
|
const float inverseM1 = 16384f / 2610f;
|
|
const float inverseM2 = 32f / 2523f;
|
|
const float c1 = 3424f / 4096f;
|
|
const float c2 = 2413f / 128f;
|
|
const float c3 = 2392f / 128f;
|
|
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
var glsl = module.ImportExtInst("GLSL.std.450");
|
|
var voidType = module.TypeVoid();
|
|
var floatType = module.TypeFloat(32);
|
|
var vec2Type = module.TypeVector(floatType, 2);
|
|
var vec3Type = module.TypeVector(floatType, 3);
|
|
var vec4Type = module.TypeVector(floatType, 4);
|
|
var inputVec4Pointer = module.TypePointer(SpirvStorageClass.Input, vec4Type);
|
|
var outputVec4Pointer = module.TypePointer(SpirvStorageClass.Output, vec4Type);
|
|
var imageType = module.TypeImage(
|
|
floatType,
|
|
SpirvImageDim.Dim2D,
|
|
depth: false,
|
|
arrayed: false,
|
|
multisampled: false,
|
|
sampled: 1,
|
|
SpirvImageFormat.Unknown);
|
|
var sampledImageType = module.TypeSampledImage(imageType);
|
|
var sampledImagePointer =
|
|
module.TypePointer(SpirvStorageClass.UniformConstant, sampledImageType);
|
|
var attribute = module.AddGlobalVariable(inputVec4Pointer, SpirvStorageClass.Input);
|
|
module.AddDecoration(attribute, SpirvDecoration.Location, 0);
|
|
var texture = module.AddGlobalVariable(
|
|
sampledImagePointer,
|
|
SpirvStorageClass.UniformConstant);
|
|
module.AddDecoration(texture, SpirvDecoration.DescriptorSet, 0);
|
|
module.AddDecoration(texture, SpirvDecoration.Binding, 1);
|
|
var output = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output);
|
|
module.AddDecoration(output, SpirvDecoration.Location, 0);
|
|
|
|
uint Float(float value) => module.ConstantFloat(floatType, value);
|
|
uint Vec3(float value) => module.ConstantComposite(
|
|
vec3Type,
|
|
Float(value),
|
|
Float(value),
|
|
Float(value));
|
|
uint Ext(uint operation, uint resultType, params uint[] operands)
|
|
{
|
|
var values = new uint[2 + operands.Length];
|
|
values[0] = glsl;
|
|
values[1] = operation;
|
|
operands.CopyTo(values, 2);
|
|
return module.AddInstruction(SpirvOp.ExtInst, resultType, values);
|
|
}
|
|
uint Component(uint vector, uint index) =>
|
|
module.AddInstruction(SpirvOp.CompositeExtract, floatType, vector, index);
|
|
uint Multiply(uint left, float right) =>
|
|
module.AddInstruction(SpirvOp.FMul, floatType, left, Float(right));
|
|
uint Add3(uint first, uint second, uint third) =>
|
|
module.AddInstruction(
|
|
SpirvOp.FAdd,
|
|
floatType,
|
|
module.AddInstruction(SpirvOp.FAdd, floatType, first, second),
|
|
third);
|
|
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddLabel();
|
|
var attributeValue = module.AddInstruction(SpirvOp.Load, vec4Type, attribute);
|
|
var coordinates = module.AddInstruction(
|
|
SpirvOp.VectorShuffle,
|
|
vec2Type,
|
|
attributeValue,
|
|
attributeValue,
|
|
0,
|
|
1);
|
|
var sampledImage = module.AddInstruction(SpirvOp.Load, sampledImageType, texture);
|
|
var color = module.AddInstruction(
|
|
SpirvOp.ImageSampleExplicitLod,
|
|
vec4Type,
|
|
sampledImage,
|
|
coordinates,
|
|
2,
|
|
Float(0));
|
|
var pq = module.AddInstruction(
|
|
SpirvOp.VectorShuffle,
|
|
vec3Type,
|
|
color,
|
|
color,
|
|
0,
|
|
1,
|
|
2);
|
|
|
|
// SMPTE ST 2084 converts normalized PQ code values to absolute luminance.
|
|
var powered = Ext(26, vec3Type, pq, Vec3(inverseM2));
|
|
var numerator = Ext(
|
|
40,
|
|
vec3Type,
|
|
module.AddInstruction(SpirvOp.FSub, vec3Type, powered, Vec3(c1)),
|
|
Vec3(0));
|
|
var denominator = module.AddInstruction(
|
|
SpirvOp.FSub,
|
|
vec3Type,
|
|
Vec3(c2),
|
|
module.AddInstruction(SpirvOp.FMul, vec3Type, Vec3(c3), powered));
|
|
var normalizedLuminance = Ext(
|
|
26,
|
|
vec3Type,
|
|
module.AddInstruction(SpirvOp.FDiv, vec3Type, numerator, denominator),
|
|
Vec3(inverseM1));
|
|
var scRgb2020 = module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
vec3Type,
|
|
normalizedLuminance,
|
|
Vec3(10000f / 80f));
|
|
|
|
var red2020 = Component(scRgb2020, 0);
|
|
var green2020 = Component(scRgb2020, 1);
|
|
var blue2020 = Component(scRgb2020, 2);
|
|
var red = Add3(
|
|
Multiply(red2020, 1.660491f),
|
|
Multiply(green2020, -0.587641f),
|
|
Multiply(blue2020, -0.072850f));
|
|
var green = Add3(
|
|
Multiply(red2020, -0.124550f),
|
|
Multiply(green2020, 1.132900f),
|
|
Multiply(blue2020, -0.008349f));
|
|
var blue = Add3(
|
|
Multiply(red2020, -0.018151f),
|
|
Multiply(green2020, -0.100579f),
|
|
Multiply(blue2020, 1.118730f));
|
|
var converted = module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
vec4Type,
|
|
red,
|
|
green,
|
|
blue,
|
|
Component(color, 3));
|
|
module.AddStatement(SpirvOp.Store, output, converted);
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
module.AddEntryPoint(
|
|
SpirvExecutionModel.Fragment,
|
|
main,
|
|
"main",
|
|
[attribute, texture, output]);
|
|
module.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft);
|
|
return module.Build();
|
|
}
|
|
|
|
public static byte[] CreateSolidFragment(float red, float green, float blue, float alpha)
|
|
{
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
|
|
var voidType = module.TypeVoid();
|
|
var floatType = module.TypeFloat(32);
|
|
var vec4Type = module.TypeVector(floatType, 4);
|
|
var outputVec4Pointer = module.TypePointer(SpirvStorageClass.Output, vec4Type);
|
|
var output = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output);
|
|
module.AddName(output, "outColor");
|
|
module.AddDecoration(output, SpirvDecoration.Location, 0);
|
|
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddName(main, "main");
|
|
module.AddLabel();
|
|
var color = module.ConstantComposite(
|
|
vec4Type,
|
|
module.ConstantFloat(floatType, red),
|
|
module.ConstantFloat(floatType, green),
|
|
module.ConstantFloat(floatType, blue),
|
|
module.ConstantFloat(floatType, alpha));
|
|
module.AddStatement(SpirvOp.Store, output, color);
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
|
|
module.AddEntryPoint(SpirvExecutionModel.Fragment, main, "main", [output]);
|
|
module.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft);
|
|
return module.Build();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Diagnostic fragment stage that exposes one interpolated vertex output
|
|
/// directly as color. This keeps the real guest vertex/index/depth path
|
|
/// intact while isolating fragment-shader translation from interface data.
|
|
/// </summary>
|
|
public static byte[] CreateAttributeFragment(uint location)
|
|
{
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
|
|
var voidType = module.TypeVoid();
|
|
var floatType = module.TypeFloat(32);
|
|
var vec4Type = module.TypeVector(floatType, 4);
|
|
var inputPointer = module.TypePointer(SpirvStorageClass.Input, vec4Type);
|
|
var outputPointer = module.TypePointer(SpirvStorageClass.Output, vec4Type);
|
|
var input = module.AddGlobalVariable(inputPointer, SpirvStorageClass.Input);
|
|
module.AddName(input, $"attr{location}");
|
|
module.AddDecoration(input, SpirvDecoration.Location, location);
|
|
var output = module.AddGlobalVariable(outputPointer, SpirvStorageClass.Output);
|
|
module.AddName(output, "outColor");
|
|
module.AddDecoration(output, SpirvDecoration.Location, 0);
|
|
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddName(main, "main");
|
|
module.AddLabel();
|
|
var value = module.AddInstruction(SpirvOp.Load, vec4Type, input);
|
|
module.AddStatement(SpirvOp.Store, output, value);
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
|
|
module.AddEntryPoint(
|
|
SpirvExecutionModel.Fragment,
|
|
main,
|
|
"main",
|
|
[input, output]);
|
|
module.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft);
|
|
return module.Build();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Minimal fragment stage for fixed-function depth-only passes. The
|
|
/// guest has no pixel shader and therefore cannot export colour; keeping
|
|
/// this stage output-free preserves that contract while allowing Vulkan
|
|
/// to run early/late depth tests for the translated vertex shader.
|
|
/// </summary>
|
|
public static byte[] CreateDepthOnlyFragment()
|
|
{
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
|
|
var voidType = module.TypeVoid();
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddName(main, "main");
|
|
module.AddLabel();
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
|
|
module.AddEntryPoint(SpirvExecutionModel.Fragment, main, "main", []);
|
|
module.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft);
|
|
return module.Build();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compute kernel that deswizzles RDNA2 tiled surfaces at 4 bytes/element into
|
|
/// a linear output buffer — one GPU thread per texel, one dispatch-Z layer per
|
|
/// array slice. Mirrors <c>GnmTiling.GetDetileParams</c> so it is bit-identical
|
|
/// to the CPU fallback for both supported equation families:
|
|
/// <code>
|
|
/// z = layer;
|
|
/// inBlock = equation == BlockTable // modes 1/4/8
|
|
/// ? blockTable[(y % blockHeight) * blockWidth + (x % blockWidth)]
|
|
/// : xTerm[x & xMask] ^ yTerm[y & yMask]; // ExactXor 5/9/24/27
|
|
/// src = z * srcSliceElements
|
|
/// + (y / blockHeight * blocksPerRow + x / blockWidth) * blockElements
|
|
/// + inBlock;
|
|
/// out[z * width * height + y * width + x] = tiled[src];
|
|
/// </code>
|
|
/// Each array slice is an independently tiled 2D surface; the caller packs the
|
|
/// slices contiguously in the tiled buffer (stride <c>srcSliceElements</c>) and
|
|
/// the output ends up layer-major, matching a single multi-layer
|
|
/// buffer->image copy. For a non-arrayed texture the caller dispatches a
|
|
/// single Z layer with <c>srcSliceElements</c> unused (z == 0).
|
|
///
|
|
/// The term tables hold ELEMENT offsets. For ExactXor the caller pre-shifts the
|
|
/// byte-unit GetDetileParams terms right by log2(bytesPerElement) (exact at 4bpp
|
|
/// since the equation's low two byte-offset bits are 0); for BlockTable the
|
|
/// GetDetileParams block table is already in element units. Binding 1 carries
|
|
/// xTerm (ExactXor) OR blockTable (BlockTable) — the two equations index
|
|
/// different-sized buffers, so the kernel branches and evaluates exactly one.
|
|
///
|
|
/// width/height are ELEMENT dims (for block-compressed formats a 4x4 block is
|
|
/// one element). Each element spans uintsPerElement = bpp/4 words (4bpp -> 1,
|
|
/// 8bpp -> 2, 16bpp -> 4); the X dispatch is widened by that factor so each
|
|
/// thread copies one word (elemX = gidX / upe, word = gidX % upe). 1/2 bpp are
|
|
/// sub-word and stay on the CPU.
|
|
///
|
|
/// Descriptor set 0: binding 0 = tiled uint[], 1 = xTerm/blockTable uint[],
|
|
/// 2 = yTerm uint[], 3 = out uint[]. Push constants (11 x uint, offset i*4):
|
|
/// width, height, blockWidth, blockHeight, blockElements, blocksPerRow,
|
|
/// xMask, yMask, srcSliceElements, equation (0 = ExactXor, 1 = BlockTable),
|
|
/// uintsPerElement. Local size 8x8x1; dispatch X = ceil(width*upe/8),
|
|
/// Y = ceil(height/8), Z = arrayLayers.
|
|
/// </summary>
|
|
public static byte[] CreateDetileCompute()
|
|
{
|
|
var module = new SpirvModuleBuilder();
|
|
module.AddCapability(SpirvCapability.Shader);
|
|
|
|
var voidType = module.TypeVoid();
|
|
var boolType = module.TypeBool();
|
|
var uintType = module.TypeInt(32, signed: false);
|
|
var uvec3Type = module.TypeVector(uintType, 3);
|
|
|
|
// One shared Block-decorated storage-buffer struct: struct { uint data[]; }.
|
|
var runtimeArray = module.TypeRuntimeArray(uintType);
|
|
module.AddDecoration(runtimeArray, SpirvDecoration.ArrayStride, 4);
|
|
var bufferStruct = module.TypeStruct(runtimeArray);
|
|
module.AddDecoration(bufferStruct, SpirvDecoration.Block);
|
|
module.AddMemberDecoration(bufferStruct, 0, SpirvDecoration.Offset, 0);
|
|
var bufferPtrType = module.TypePointer(SpirvStorageClass.StorageBuffer, bufferStruct);
|
|
var uintStoragePtr = module.TypePointer(SpirvStorageClass.StorageBuffer, uintType);
|
|
|
|
uint MakeBuffer(uint binding, string name)
|
|
{
|
|
var variable = module.AddGlobalVariable(bufferPtrType, SpirvStorageClass.StorageBuffer);
|
|
module.AddName(variable, name);
|
|
module.AddDecoration(variable, SpirvDecoration.DescriptorSet, 0);
|
|
module.AddDecoration(variable, SpirvDecoration.Binding, binding);
|
|
return variable;
|
|
}
|
|
|
|
var tiledVar = MakeBuffer(0, "tiled");
|
|
var xTermVar = MakeBuffer(1, "xTerm");
|
|
var yTermVar = MakeBuffer(2, "yTerm");
|
|
var outVar = MakeBuffer(3, "outLinear");
|
|
|
|
// Push constants: struct { uint p0..p10; }, each member at offset i*4.
|
|
var pushStruct = module.TypeStruct(
|
|
uintType, uintType, uintType, uintType, uintType, uintType,
|
|
uintType, uintType, uintType, uintType, uintType);
|
|
module.AddDecoration(pushStruct, SpirvDecoration.Block);
|
|
for (uint member = 0; member < 11; member++)
|
|
{
|
|
module.AddMemberDecoration(pushStruct, member, SpirvDecoration.Offset, member * 4);
|
|
}
|
|
|
|
var pushPtrType = module.TypePointer(SpirvStorageClass.PushConstant, pushStruct);
|
|
var pushMemberPtrType = module.TypePointer(SpirvStorageClass.PushConstant, uintType);
|
|
var pushVar = module.AddGlobalVariable(pushPtrType, SpirvStorageClass.PushConstant);
|
|
module.AddName(pushVar, "pc");
|
|
|
|
var inputUvec3Ptr = module.TypePointer(SpirvStorageClass.Input, uvec3Type);
|
|
var gidVar = module.AddGlobalVariable(inputUvec3Ptr, SpirvStorageClass.Input);
|
|
module.AddName(gidVar, "gid");
|
|
module.AddDecoration(gidVar, SpirvDecoration.BuiltIn, (uint)SpirvBuiltIn.GlobalInvocationId);
|
|
|
|
var uintConst = new uint[11];
|
|
for (uint value = 0; value < 11; value++)
|
|
{
|
|
uintConst[value] = module.Constant(uintType, value);
|
|
}
|
|
|
|
var functionType = module.TypeFunction(voidType);
|
|
var main = module.BeginFunction(voidType, functionType);
|
|
module.AddName(main, "main");
|
|
module.AddLabel();
|
|
|
|
var gid = module.AddInstruction(SpirvOp.Load, uvec3Type, gidVar);
|
|
var gidX = module.AddInstruction(SpirvOp.CompositeExtract, uintType, gid, 0);
|
|
var y = module.AddInstruction(SpirvOp.CompositeExtract, uintType, gid, 1);
|
|
var z = module.AddInstruction(SpirvOp.CompositeExtract, uintType, gid, 2);
|
|
|
|
uint PushField(uint index)
|
|
{
|
|
var pointer = module.AddInstruction(
|
|
SpirvOp.AccessChain, pushMemberPtrType, pushVar, uintConst[index]);
|
|
return module.AddInstruction(SpirvOp.Load, uintType, pointer);
|
|
}
|
|
|
|
// width/height are ELEMENT dims (for BC, a 4x4 block is one element). Each
|
|
// element spans uintsPerElement 32-bit words (bpp/4: 4bpp->1, 8bpp->2,
|
|
// 16bpp->4). The X dispatch is widened by uintsPerElement so each thread
|
|
// copies exactly one word: elemX = gidX / upe, wordIndex = gidX % upe.
|
|
var width = PushField(0);
|
|
var height = PushField(1);
|
|
var blockWidth = PushField(2);
|
|
var blockHeight = PushField(3);
|
|
var blockElements = PushField(4);
|
|
var blocksPerRow = PushField(5);
|
|
var xMask = PushField(6);
|
|
var yMask = PushField(7);
|
|
var srcSliceElements = PushField(8);
|
|
var equation = PushField(9);
|
|
var uintsPerElement = PushField(10);
|
|
|
|
var elemX = module.AddInstruction(SpirvOp.UDiv, uintType, gidX, uintsPerElement);
|
|
var elemXTimesUpe = module.AddInstruction(SpirvOp.IMul, uintType, elemX, uintsPerElement);
|
|
var wordIndex = module.AddInstruction(SpirvOp.ISub, uintType, gidX, elemXTimesUpe);
|
|
|
|
var xInRange = module.AddInstruction(SpirvOp.ULessThan, boolType, elemX, width);
|
|
var yInRange = module.AddInstruction(SpirvOp.ULessThan, boolType, y, height);
|
|
var inRange = module.AddInstruction(SpirvOp.LogicalAnd, boolType, xInRange, yInRange);
|
|
|
|
var bodyLabel = module.AllocateId();
|
|
var mergeLabel = module.AllocateId();
|
|
module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
|
|
module.AddStatement(SpirvOp.BranchConditional, inRange, bodyLabel, mergeLabel);
|
|
|
|
module.AddLabel(bodyLabel);
|
|
|
|
// blockIdx = (y / blockHeight) * blocksPerRow + (elemX / blockWidth)
|
|
var yDiv = module.AddInstruction(SpirvOp.UDiv, uintType, y, blockHeight);
|
|
var blockRow = module.AddInstruction(SpirvOp.IMul, uintType, yDiv, blocksPerRow);
|
|
var xDiv = module.AddInstruction(SpirvOp.UDiv, uintType, elemX, blockWidth);
|
|
var blockIdx = module.AddInstruction(SpirvOp.IAdd, uintType, blockRow, xDiv);
|
|
|
|
// off (element offset within the block) = equation == BlockTable
|
|
// ? blockTable[(y % blockHeight) * blockWidth + (elemX % blockWidth)]
|
|
// : xTerm[elemX & xMask] ^ yTerm[y & yMask]
|
|
// Binding 1 (xTermVar) doubles as the block table; the two equations index
|
|
// different-sized buffers, so exactly one branch executes (no OOB read).
|
|
var isBlockTable = module.AddInstruction(SpirvOp.INotEqual, boolType, equation, uintConst[0]);
|
|
var xorLabel = module.AllocateId();
|
|
var tableLabel = module.AllocateId();
|
|
var offMergeLabel = module.AllocateId();
|
|
module.AddStatement(SpirvOp.SelectionMerge, offMergeLabel, 0);
|
|
module.AddStatement(SpirvOp.BranchConditional, isBlockTable, tableLabel, xorLabel);
|
|
|
|
// ExactXor: xTerm[elemX & xMask] ^ yTerm[y & yMask]
|
|
module.AddLabel(xorLabel);
|
|
var xIdx = module.AddInstruction(SpirvOp.BitwiseAnd, uintType, elemX, xMask);
|
|
var xPtr = module.AddInstruction(SpirvOp.AccessChain, uintStoragePtr, xTermVar, uintConst[0], xIdx);
|
|
var xTerm = module.AddInstruction(SpirvOp.Load, uintType, xPtr);
|
|
var yIdx = module.AddInstruction(SpirvOp.BitwiseAnd, uintType, y, yMask);
|
|
var yPtr = module.AddInstruction(SpirvOp.AccessChain, uintStoragePtr, yTermVar, uintConst[0], yIdx);
|
|
var yTerm = module.AddInstruction(SpirvOp.Load, uintType, yPtr);
|
|
var offXor = module.AddInstruction(SpirvOp.BitwiseXor, uintType, xTerm, yTerm);
|
|
module.AddStatement(SpirvOp.Branch, offMergeLabel);
|
|
|
|
// BlockTable: blockTable[inY * blockWidth + inX], inX/inY = position in block
|
|
module.AddLabel(tableLabel);
|
|
var blockXBase = module.AddInstruction(SpirvOp.IMul, uintType, xDiv, blockWidth);
|
|
var inX = module.AddInstruction(SpirvOp.ISub, uintType, elemX, blockXBase);
|
|
var blockYBase = module.AddInstruction(SpirvOp.IMul, uintType, yDiv, blockHeight);
|
|
var inY = module.AddInstruction(SpirvOp.ISub, uintType, y, blockYBase);
|
|
var rowInBlock = module.AddInstruction(SpirvOp.IMul, uintType, inY, blockWidth);
|
|
var tableIdx = module.AddInstruction(SpirvOp.IAdd, uintType, rowInBlock, inX);
|
|
var tablePtr = module.AddInstruction(SpirvOp.AccessChain, uintStoragePtr, xTermVar, uintConst[0], tableIdx);
|
|
var offTable = module.AddInstruction(SpirvOp.Load, uintType, tablePtr);
|
|
module.AddStatement(SpirvOp.Branch, offMergeLabel);
|
|
|
|
module.AddLabel(offMergeLabel);
|
|
var off = module.AddInstruction(SpirvOp.Phi, uintType, offXor, xorLabel, offTable, tableLabel);
|
|
|
|
// srcElem = z * srcSliceElements + blockIdx * blockElements + off (in elements)
|
|
// srcWord = srcElem * uintsPerElement + wordIndex
|
|
var srcSliceBase = module.AddInstruction(SpirvOp.IMul, uintType, z, srcSliceElements);
|
|
var blockBase = module.AddInstruction(SpirvOp.IMul, uintType, blockIdx, blockElements);
|
|
var srcInSlice = module.AddInstruction(SpirvOp.IAdd, uintType, blockBase, off);
|
|
var srcElem = module.AddInstruction(SpirvOp.IAdd, uintType, srcSliceBase, srcInSlice);
|
|
var srcElemWords = module.AddInstruction(SpirvOp.IMul, uintType, srcElem, uintsPerElement);
|
|
var src = module.AddInstruction(SpirvOp.IAdd, uintType, srcElemWords, wordIndex);
|
|
var srcPtr = module.AddInstruction(SpirvOp.AccessChain, uintStoragePtr, tiledVar, uintConst[0], src);
|
|
var word = module.AddInstruction(SpirvOp.Load, uintType, srcPtr);
|
|
|
|
// dstElem = z * width * height + y * width + elemX (in elements)
|
|
// dstWord = dstElem * uintsPerElement + wordIndex
|
|
var sliceElements = module.AddInstruction(SpirvOp.IMul, uintType, width, height);
|
|
var dstSliceBase = module.AddInstruction(SpirvOp.IMul, uintType, z, sliceElements);
|
|
var rowBase = module.AddInstruction(SpirvOp.IMul, uintType, y, width);
|
|
var dstRow = module.AddInstruction(SpirvOp.IAdd, uintType, rowBase, elemX);
|
|
var dstElem = module.AddInstruction(SpirvOp.IAdd, uintType, dstSliceBase, dstRow);
|
|
var dstElemWords = module.AddInstruction(SpirvOp.IMul, uintType, dstElem, uintsPerElement);
|
|
var dstIdx = module.AddInstruction(SpirvOp.IAdd, uintType, dstElemWords, wordIndex);
|
|
var dstPtr = module.AddInstruction(SpirvOp.AccessChain, uintStoragePtr, outVar, uintConst[0], dstIdx);
|
|
module.AddStatement(SpirvOp.Store, dstPtr, word);
|
|
|
|
module.AddStatement(SpirvOp.Branch, mergeLabel);
|
|
module.AddLabel(mergeLabel);
|
|
module.AddStatement(SpirvOp.Return);
|
|
module.EndFunction();
|
|
|
|
module.AddExecutionMode(main, SpirvExecutionMode.LocalSize, 8, 8, 1);
|
|
module.AddEntryPoint(
|
|
SpirvExecutionModel.GLCompute,
|
|
main,
|
|
"main",
|
|
[gidVar, tiledVar, xTermVar, yTermVar, outVar, pushVar]);
|
|
return module.Build();
|
|
}
|
|
}
|