refactor(shader): emit SPIR-V declarations on demand

This commit is contained in:
nmzik
2026-08-19 04:57:23 +02:00
parent e1bcc298a0
commit d34e30d892
20 changed files with 1629 additions and 1495 deletions
@@ -78,10 +78,10 @@ uint32_t AddressU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::
auto value = ctx.Def(address.Arg(packed));
if (ComponentUsesA16(mem, component)) {
if ((half & 1u) != 0u) {
value = Binary(ctx.state, OpShiftRightLogical, ctx.state.uint_type, value,
value = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), value,
ConstantU32(ctx.state, 16));
}
value = Binary(ctx.state, OpBitwiseAnd, ctx.state.uint_type, value,
value = Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), value,
ConstantU32(ctx.state, 0xffffu));
}
return value;
@@ -92,7 +92,7 @@ uint32_t AddressF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::
const auto value = AddressU32(ctx, mem, address, component);
return ComponentUsesA16(mem, component)
? EmitF16BitsToF32(ctx.state, value)
: Unary(ctx.state, OpBitcast, ctx.state.float_type, value);
: Unary(ctx.state, OpBitcast, TypeF32(ctx.state), value);
}
ImageSampleLayout Layout(const IR::MemoryInfo& mem, ImageViewKind view) {
@@ -117,28 +117,20 @@ uint32_t ZeroF32(EmitterState& state) {
return ConstantF32(state, 0);
}
uint32_t ZeroU32x4(EmitterState& state) {
const auto zero = ConstantU32(state, 0);
const auto value = state.builder.AllocateId();
state.builder.AddType(
{OpConstantComposite, state.vec4_uint_type, value, zero, zero, zero, zero});
return value;
}
uint32_t CubeAxis(EmitterState& state, uint32_t value) {
return Binary(state, OpFSub, state.float_type, value, ConstantF32(state, 0x3f800000u));
return Binary(state, OpFSub, TypeF32(state), value, ConstantF32(state, 0x3f800000u));
}
uint32_t CubeLayer(EmitterState& state, uint32_t value) {
const auto guest = state.builder.AllocateId();
state.builder.AddFunction({OpConvertFToU, state.uint_type, guest, value});
state.builder.AddFunction({OpConvertFToU, TypeU32(state), guest, value});
const auto padding =
Binary(state, OpShiftLeftLogical, state.uint_type,
Binary(state, OpShiftRightLogical, state.uint_type, guest, ConstantU32(state, 3)),
Binary(state, OpShiftLeftLogical, TypeU32(state),
Binary(state, OpShiftRightLogical, TypeU32(state), guest, ConstantU32(state, 3)),
ConstantU32(state, 1));
const auto host = Binary(state, OpISub, state.uint_type, guest, padding);
const auto host = Binary(state, OpISub, TypeU32(state), guest, padding);
const auto result = state.builder.AllocateId();
state.builder.AddFunction({OpConvertUToF, state.float_type, result, host});
state.builder.AddFunction({OpConvertUToF, TypeF32(state), result, host});
return result;
}
@@ -159,10 +151,10 @@ uint32_t CoordF32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::In
: ZeroF32(ctx.state);
if (mem.image_cube) z = CubeLayer(ctx.state, z);
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec3_float_type, result, x, y, z});
{OpCompositeConstruct, TypeF32Vector(ctx.state, 3), result, x, y, z});
} else {
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec2_float_type, result, x, y});
{OpCompositeConstruct, TypeF32Vector(ctx.state, 2), result, x, y});
}
return result;
}
@@ -179,10 +171,10 @@ uint32_t CoordU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::In
const auto z = mem.image_address_components > 2u ? AddressU32(ctx, mem, address, 2)
: ConstantU32(ctx.state, 0);
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec3_uint_type, result, x, y, z});
{OpCompositeConstruct, TypeU32Vector(ctx.state, 3), result, x, y, z});
} else {
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec2_uint_type, result, x, y});
{OpCompositeConstruct, TypeU32Vector(ctx.state, 2), result, x, y});
}
return result;
}
@@ -196,7 +188,7 @@ uint32_t LodU32(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst
}
uint32_t FloatBits(ValueEmitContext& ctx, uint32_t value) {
return Unary(ctx.state, OpBitcast, ctx.state.uint_type, value);
return Unary(ctx.state, OpBitcast, TypeU32(ctx.state), value);
}
uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool dref) {
@@ -208,12 +200,12 @@ uint32_t ResultVector(ValueEmitContext& ctx, uint32_t value, bool integer, bool
}
const auto scalar = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction({OpCompositeExtract,
integer ? ctx.state.uint_type : ctx.state.float_type, scalar,
integer ? TypeU32(ctx.state) : TypeF32(ctx.state), scalar,
value, index});
component[index] = integer ? scalar : FloatBits(ctx, scalar);
}
const auto result = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction({OpCompositeConstruct, ctx.state.vec4_uint_type, result,
ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), result,
component[0], component[1], component[2], component[3]});
return result;
}
@@ -240,15 +232,15 @@ uint32_t QueryDimensions(ValueEmitContext& ctx, const IR::Inst& inst, const IR::
for (uint32_t index = 0; index < components; index++) {
result[index] = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction(
{OpCompositeExtract, ctx.state.uint_type, result[index], size, index});
{OpCompositeExtract, TypeU32(ctx.state), result[index], size, index});
}
}
if (ImageSpirvMultisampled(view) == 0u) {
result[3] = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction({OpImageQueryLevels, ctx.state.uint_type, result[3], image});
ctx.state.builder.AddFunction({OpImageQueryLevels, TypeU32(ctx.state), result[3], image});
}
const auto vector = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction({OpCompositeConstruct, ctx.state.vec4_uint_type, vector,
ctx.state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(ctx.state, 4), vector,
result[0], result[1], result[2], result[3]});
return vector;
}
@@ -262,55 +254,51 @@ uint32_t PackedOffset(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR
const auto result = ctx.state.builder.AllocateId();
if (components == 3u) {
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec3_int_type, result, zero, zero, zero});
{OpCompositeConstruct, TypeI32Vector(ctx.state, 3), result, zero, zero, zero});
} else {
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec2_int_type, result, zero, zero});
{OpCompositeConstruct, TypeI32Vector(ctx.state, 2), result, zero, zero});
}
return result;
}
const auto packed = Unary(ctx.state, OpBitcast, ctx.state.int_type,
const auto packed = Unary(ctx.state, OpBitcast, TypeI32(ctx.state),
AddressU32(ctx, mem, address, layout.offset));
uint32_t values[3] = {zero, zero, zero};
for (uint32_t index = 0; index < components; index++) {
values[index] = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction({OpBitFieldSExtract, ctx.state.int_type, values[index],
ctx.state.builder.AddFunction({OpBitFieldSExtract, TypeI32(ctx.state), values[index],
packed, ConstantU32(ctx.state, index * 8u),
ConstantU32(ctx.state, 6)});
}
if (components == 1u) return values[0];
const auto result = ctx.state.builder.AllocateId();
if (components == 3u) {
ctx.state.builder.AddFunction({OpCompositeConstruct, ctx.state.vec3_int_type, result,
ctx.state.builder.AddFunction({OpCompositeConstruct, TypeI32Vector(ctx.state, 3), result,
values[0], values[1], values[2]});
} else {
ctx.state.builder.AddFunction(
{OpCompositeConstruct, ctx.state.vec2_int_type, result, values[0], values[1]});
{OpCompositeConstruct, TypeI32Vector(ctx.state, 2), result, values[0], values[1]});
}
return result;
}
uint32_t HorizontalOffsets(EmitterState& state, ImageViewKind view) {
const auto components = ImageViewSpatialComponents(view);
const auto array_type = state.builder.AllocateId();
const auto count = ConstantU32(state, 4);
const auto element_type = components == 1u ? state.int_type : state.vec2_int_type;
state.builder.AddType({OpTypeArray, array_type, element_type, count});
uint32_t offsets[4] {};
const auto element_type = components == 1u ? TypeI32(state) : TypeI32Vector(state, 2);
const auto array_type = state.builder.Type(OpTypeArray, {element_type, count});
uint32_t offsets[4] {};
for (uint32_t index = 0; index < 4u; index++) {
const auto x = ConstantI32(state, static_cast<int32_t>(index) - 1);
if (components == 1u) {
offsets[index] = x;
} else {
offsets[index] = state.builder.AllocateId();
state.builder.AddType({OpConstantComposite, state.vec2_int_type, offsets[index], x,
ConstantI32(state, 0)});
offsets[index] = state.builder.Constant(OpConstantComposite, TypeI32Vector(state, 2),
{x, ConstantI32(state, 0)});
}
}
const auto value = state.builder.AllocateId();
state.builder.AddType(
{OpConstantComposite, array_type, value, offsets[0], offsets[1], offsets[2], offsets[3]});
return value;
return state.builder.Constant(OpConstantComposite, array_type,
{offsets[0], offsets[1], offsets[2], offsets[3]});
}
uint32_t InverseSwizzle(uint32_t swizzle, uint32_t component) {
@@ -331,14 +319,14 @@ uint32_t StoreTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t d
const auto packed_index = DmaskComponentIndex(dmask, source);
raw = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction(
{OpCompositeExtract, ctx.state.uint_type, raw, data, packed_index});
{OpCompositeExtract, TypeU32(ctx.state), raw, data, packed_index});
}
values[component] = integer ? raw : Unary(ctx.state, OpBitcast, ctx.state.float_type, raw);
values[component] = integer ? raw : Unary(ctx.state, OpBitcast, TypeF32(ctx.state), raw);
}
const auto texel = ctx.state.builder.AllocateId();
ctx.state.builder.AddFunction({OpCompositeConstruct,
integer ? ctx.state.vec4_uint_type : ctx.state.vec4_float_type,
texel, values[0], values[1], values[2], values[3]});
ctx.state.builder.AddFunction(
{OpCompositeConstruct, integer ? TypeU32Vector(ctx.state, 4) : TypeF32Vector(ctx.state, 4),
texel, values[0], values[1], values[2], values[3]});
return texel;
}
@@ -372,26 +360,27 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto* address = ctx.ImageAddress(inst.Arg(sampled ? 2 : 1));
if (address == nullptr) return true;
if (op == IR::ValueOpcode::ImageQueryDimensions) {
state.builder.RequireCapability(CapabilityImageQuery);
ctx.Define(inst, QueryDimensions(ctx, inst, mem, *address));
return true;
}
if (op == IR::ValueOpcode::ImageQueryLod) {
state.builder.RequireCapability(CapabilityImageQuery);
const auto view = SampledImageViewKind(state, mem, pc);
const auto sampled = MakeSampledImage(state, mem, pc, view);
const auto lod = state.builder.AllocateId();
state.builder.AddFunction(
{OpImageQueryLod, state.vec2_float_type, lod, sampled,
{OpImageQueryLod, TypeF32Vector(state, 2), lod, sampled,
CoordF32(ctx, mem, *address, 0, ImageViewSpatialComponents(view))});
uint32_t values[4] = {ConstantU32(state, 0), ConstantU32(state, 0), ConstantU32(state, 0),
ConstantU32(state, 0)};
for (uint32_t index = 0; index < 2u; index++) {
const auto component = state.builder.AllocateId();
state.builder.AddFunction(
{OpCompositeExtract, state.float_type, component, lod, index});
state.builder.AddFunction({OpCompositeExtract, TypeF32(state), component, lod, index});
values[index] = FloatBits(ctx, component);
}
const auto result = state.builder.AllocateId();
state.builder.AddFunction({OpCompositeConstruct, state.vec4_uint_type, result, values[0],
state.builder.AddFunction({OpCompositeConstruct, TypeU32Vector(state, 4), result, values[0],
values[1], values[2], values[3]});
ctx.Define(inst, result);
return true;
@@ -403,20 +392,21 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
ctx.Define(
inst,
EmitValueOrDefaultIfCondition(
state, condition, state.vec4_uint_type, ZeroU32x4(state), [&]() {
state, condition, TypeU32Vector(state, 4), ConstantU32x4Zero(state), [&]() {
const auto image = LoadSampledImageDescriptor(state, mem, pc, view);
const auto color = state.builder.AllocateId();
const auto coord = CoordU32(ctx, mem, *address, view);
if (ImageSpirvMultisampled(view) != 0u) {
state.builder.AddFunction(
{OpImageFetch, integer ? state.vec4_uint_type : state.vec4_float_type,
color, image, coord, ImageOperandsSampleMask,
{OpImageFetch,
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color,
image, coord, ImageOperandsSampleMask,
AddressU32(ctx, mem, *address, ImageViewCoordinateComponents(view))});
} else {
state.builder.AddFunction(
{OpImageFetch, integer ? state.vec4_uint_type : state.vec4_float_type,
color, image, coord, ImageOperandsLodMask,
LodU32(ctx, mem, *address, view)});
{OpImageFetch,
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4), color,
image, coord, ImageOperandsLodMask, LodU32(ctx, mem, *address, view)});
}
return ResultVector(ctx, color, integer, false);
}));
@@ -448,7 +438,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto sample = state.builder.AllocateId();
std::vector<uint32_t> words =
dref ? std::vector<uint32_t> {OpImageDrefGather,
state.vec4_float_type,
TypeF32Vector(state, 4),
sample,
sampled,
coord,
@@ -456,9 +446,12 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
? AddressF32(ctx, mem, *address, layout.dref)
: ZeroF32(state)}
: std::vector<uint32_t> {
OpImageGather, integer ? state.vec4_uint_type : state.vec4_float_type,
sample, sampled,
coord, ConstantU32(state, ImageGatherComponent(mem.dmask))};
OpImageGather,
integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4),
sample,
sampled,
coord,
ConstantU32(state, ImageGatherComponent(mem.dmask))};
if (HasFlag(mem, Decoder::ImageSampleFlagGatherHorizontal)) {
words.push_back(ImageOperandsConstOffsetsMask);
words.push_back(HorizontalOffsets(state, view));
@@ -478,7 +471,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
? (dref ? OpImageSampleDrefExplicitLod : OpImageSampleExplicitLod)
: (dref ? OpImageSampleDrefImplicitLod : OpImageSampleImplicitLod);
const auto result_type =
dref ? state.float_type : (integer ? state.vec4_uint_type : state.vec4_float_type);
dref ? TypeF32(state) : (integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4));
const auto dref_value =
dref ? (layout.dref != NoImageComponent ? AddressF32(ctx, mem, *address, layout.dref)
: ZeroF32(state))
@@ -537,10 +530,11 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
}
const auto LoadMapping = [&](uint32_t index) {
const auto pointer = state.builder.AllocateId();
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer,
state.flattened_srt_variable, ConstantU32(state, 0), index});
state.builder.AddFunction({OpAccessChain, TypeStorageBufferElementPointer(state),
pointer, state.flattened_srt_variable, ConstantU32(state, 0),
index});
const auto value = state.builder.AllocateId();
state.builder.AddFunction({OpLoad, state.uint_type, value, pointer});
state.builder.AddFunction({OpLoad, TypeU32(state), value, pointer});
return value;
};
const auto mapping = ConstantU32(state, image.indirect_mapping_offset);
@@ -549,39 +543,38 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
auto selected = ConstantU32(state, 0u);
for (uint32_t iteration = 0; iteration < std::bit_width(image.indirect_mapping_capacity);
iteration++) {
const auto active = Binary(state, OpULessThan, state.bool_type, low, high);
const auto active = Binary(state, OpULessThan, TypeBool(state), low, high);
const auto mid =
Binary(state, OpShiftRightLogical, state.uint_type,
Binary(state, OpIAdd, state.uint_type, low, high), ConstantU32(state, 1u));
Binary(state, OpShiftRightLogical, TypeU32(state),
Binary(state, OpIAdd, TypeU32(state), low, high), ConstantU32(state, 1u));
const auto probe = state.builder.AllocateId();
state.builder.AddFunction(
{OpSelect, state.uint_type, probe, active, mid, ConstantU32(state, 0u)});
const auto entry = Binary(state, OpIAdd, state.uint_type, mapping,
Binary(state, OpIAdd, state.uint_type,
Binary(state, OpShiftLeftLogical, state.uint_type,
{OpSelect, TypeU32(state), probe, active, mid, ConstantU32(state, 0u)});
const auto entry = Binary(state, OpIAdd, TypeU32(state), mapping,
Binary(state, OpIAdd, TypeU32(state),
Binary(state, OpShiftLeftLogical, TypeU32(state),
probe, ConstantU32(state, 1u)),
ConstantU32(state, 1u)));
const auto mapped_key = LoadMapping(entry);
const auto candidate =
LoadMapping(Binary(state, OpIAdd, state.uint_type, entry, ConstantU32(state, 1u)));
const auto equal = Binary(state, OpIEqual, state.bool_type, mapped_key, key);
const auto match = Binary(state, OpLogicalAnd, state.bool_type, active, equal);
LoadMapping(Binary(state, OpIAdd, TypeU32(state), entry, ConstantU32(state, 1u)));
const auto equal = Binary(state, OpIEqual, TypeBool(state), mapped_key, key);
const auto match = Binary(state, OpLogicalAnd, TypeBool(state), active, equal);
const auto next_selected = state.builder.AllocateId();
state.builder.AddFunction(
{OpSelect, state.uint_type, next_selected, match, candidate, selected});
{OpSelect, TypeU32(state), next_selected, match, candidate, selected});
selected = next_selected;
const auto less = Binary(state, OpULessThan, state.bool_type, mapped_key, key);
const auto take_upper = Binary(state, OpLogicalAnd, state.bool_type, active, less);
const auto take_lower = Binary(state, OpLogicalAnd, state.bool_type, active,
Unary(state, OpLogicalNot, state.bool_type, less));
const auto less = Binary(state, OpULessThan, TypeBool(state), mapped_key, key);
const auto take_upper = Binary(state, OpLogicalAnd, TypeBool(state), active, less);
const auto take_lower = Binary(state, OpLogicalAnd, TypeBool(state), active,
Unary(state, OpLogicalNot, TypeBool(state), less));
const auto next_low = state.builder.AllocateId();
state.builder.AddFunction(
{OpSelect, state.uint_type, next_low, take_upper,
Binary(state, OpIAdd, state.uint_type, mid, ConstantU32(state, 1u)), low});
{OpSelect, TypeU32(state), next_low, take_upper,
Binary(state, OpIAdd, TypeU32(state), mid, ConstantU32(state, 1u)), low});
low = next_low;
const auto next_high = state.builder.AllocateId();
state.builder.AddFunction(
{OpSelect, state.uint_type, next_high, take_lower, mid, high});
state.builder.AddFunction({OpSelect, TypeU32(state), next_high, take_lower, mid, high});
high = next_high;
}
const auto default_label = state.builder.AllocateId();
@@ -616,12 +609,14 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto view = StorageImageViewKind(state, mem, true, pc);
ctx.Define(inst, EmitValueOrZeroIfCondition(state, ctx.Arg(inst, 3), [&]() {
const auto pointer = state.builder.AllocateId();
const auto pointer_type =
state.builder.Type(OpTypePointer, {StorageClassImage, TypeU32(state)});
state.builder.AddFunction(
{OpImageTexelPointer, state.ptr_image_uint, pointer,
{OpImageTexelPointer, pointer_type, pointer,
StorageImageDescriptorPointer(state, mem.resource, true, pc, view),
CoordU32(ctx, mem, *address, view), ConstantU32(state, 0)});
const auto old = state.builder.AllocateId();
state.builder.AddFunction({atomic_opcode, state.uint_type, old, pointer,
state.builder.AddFunction({atomic_opcode, TypeU32(state), old, pointer,
ConstantU32(state, ScopeDevice),
ConstantU32(state, MemorySemanticsNone),
ctx.Arg(inst, 2)});