shader: read sample IDs from pixel ancillary input

This commit is contained in:
nmzik
2026-09-06 06:54:38 +04:00
parent ace877ce28
commit b346f58b73
8 changed files with 91 additions and 20 deletions
@@ -72,7 +72,7 @@ uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t
return bits;
}
if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex ||
kind == IR::StageInputKind::Layer) {
kind == IR::StageInputKind::Layer || kind == IR::StageInputKind::SampleId) {
const auto value = state.builder.AllocateId();
const auto bits = state.builder.AllocateId();
state.builder.AddFunction({OpLoad, TypeI32(state), value, variable});
@@ -43,6 +43,7 @@ enum : uint32_t {
CapabilityImageGatherExtended = 25,
CapabilityClipDistance = 32,
CapabilityCullDistance = 33,
CapabilitySampleRateShading = 35,
CapabilitySampled1D = 43,
CapabilityImage1D = 44,
CapabilityImageQuery = 50,
@@ -92,6 +93,7 @@ enum : uint32_t {
BuiltInLayer = 9,
BuiltInFragCoord = 15,
BuiltInFrontFacing = 17,
BuiltInSampleId = 18,
BuiltInSampleMask = 20,
BuiltInFragDepth = 22,
BuiltInWorkgroupId = 26,
@@ -512,6 +512,7 @@ uint32_t BuiltInForInput(IR::StageInputKind kind) {
case IR::StageInputKind::FragCoord: return BuiltInFragCoord;
case IR::StageInputKind::FrontFacing: return BuiltInFrontFacing;
case IR::StageInputKind::Layer: return BuiltInLayer;
case IR::StageInputKind::SampleId: return BuiltInSampleId;
case IR::StageInputKind::BaryCoordSmooth: return BuiltInBaryCoordKHR;
case IR::StageInputKind::BaryCoordNoPerspective: return BuiltInBaryCoordNoPerspKHR;
case IR::StageInputKind::WorkgroupId: return BuiltInWorkgroupId;
@@ -535,7 +536,7 @@ void AddInputAnnotationsAndNames(EmitterState& state) {
}
for (const auto& input: state.inputs) {
state.builder.AddName(input.variable_id, input.debug_name.c_str());
if (input.kind == IR::StageInputKind::Layer) {
if (input.kind == IR::StageInputKind::Layer || input.kind == IR::StageInputKind::SampleId) {
state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat});
}
if (input.kind == IR::StageInputKind::Parameter) {
@@ -711,6 +712,9 @@ void DefineModule(EmitterState& state) {
state.builder.RequireVersion(0x00010500u);
state.builder.RequireCapability(CapabilityShaderLayer);
}
if (InputVariableForKind(state, IR::StageInputKind::SampleId) != 0) {
state.builder.RequireCapability(CapabilitySampleRateShading);
}
if (state.requirements.image_gather_extended) {
state.builder.RequireCapability(CapabilityImageGatherExtended);
}
@@ -803,6 +807,7 @@ void DefineModule(EmitterState& state) {
case IR::StageInputKind::VertexIndex:
case IR::StageInputKind::InstanceIndex:
case IR::StageInputKind::Layer:
case IR::StageInputKind::SampleId:
ptr_type = TypePointer(state, StorageClassInput, TypeI32(state));
break;
case IR::StageInputKind::WorkgroupId:
@@ -162,6 +162,7 @@ enum class StageInputKind {
FrontFacing,
PackedAncillary,
Layer,
SampleId,
BaryCoordSmooth,
BaryCoordNoPerspective,
WorkgroupId,
@@ -310,17 +310,24 @@ void FoldInstruction(Block& block, Block::iterator instruction) {
const auto* source = value.TryInstruction();
if (source != nullptr && source->GetOpcode() == ValueOpcode::GetBuiltin &&
source->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::PackedAncillary)) &&
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) &&
offset.U32() >= 16u && offset.U32() < 27u && count.U32() != 0u &&
count.U32() <= 27u - offset.U32()) {
// Preserve the extraction (including sign extension) while exposing only its
// used hardware field. Other live ancillary fields remain unsupported.
const auto layer = block.PrependNewInst(
instruction, ValueOpcode::GetBuiltin,
{Value(static_cast<uint32_t>(StageInputKind::Layer)), Value(0u)});
inst.SetArg(0, Value(&*layer));
inst.SetArg(1, Value(offset.U32() - 16u));
return;
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) && count.U32() != 0u) {
constexpr struct {
uint32_t start;
uint32_t end;
StageInputKind kind;
} fields[] = {{8u, 12u, StageInputKind::SampleId}, {16u, 27u, StageInputKind::Layer}};
for (const auto& field: fields) {
if (offset.U32() >= field.start && offset.U32() < field.end &&
count.U32() <= field.end - offset.U32()) {
// Preserve extraction and sign extension while exposing only the used field.
const auto input = block.PrependNewInst(
instruction, ValueOpcode::GetBuiltin,
{Value(static_cast<uint32_t>(field.kind)), Value(0u)});
inst.SetArg(0, Value(&*input));
inst.SetArg(1, Value(offset.U32() - field.start));
return;
}
}
}
if (!IsImmediate(value, Type::U32) || !IsImmediate(offset, Type::U32) ||
!IsImmediate(count, Type::U32) || offset.U32() > 32u ||
@@ -118,8 +118,9 @@ void ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
case StageInputKind::PackedAncillary:
return Fail("packed pixel ancillary input has an unsupported live use");
case StageInputKind::Layer:
case StageInputKind::SampleId:
if (program.stage != ShaderType::Pixel || component != 0u) {
return Fail("typed layer input is invalid");
return Fail("typed pixel scalar input is invalid");
}
break;
case StageInputKind::VertexIndex:
@@ -259,6 +260,7 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
AddInput(info, kind, 0, 1, "gl_FrontFacing");
break;
case StageInputKind::Layer: AddInput(info, kind, 0, 1, "gl_Layer"); break;
case StageInputKind::SampleId: AddInput(info, kind, 0, 1, "gl_SampleID"); break;
case StageInputKind::BaryCoordSmooth:
AddInput(info, kind, 0, 3, "gl_BaryCoordKHR");
break;