shader: share finite descriptor planning with GPU-selected inline samplers

This commit is contained in:
nmzik
2026-10-09 16:51:10 +02:00
parent bb5edbd490
commit 8c81386568
9 changed files with 383 additions and 56 deletions
@@ -137,6 +137,15 @@ uint32_t ImageViewSizeType(EmitterState& state, ImageDimension dimension) {
}
}
namespace {
void DecorateNonUniformDescriptor(EmitterState& state, uint32_t id) {
state.builder.RequireExtension("SPV_EXT_descriptor_indexing");
state.builder.RequireCapability(spv::CapabilityShaderNonUniform);
state.builder.RequireCapability(spv::CapabilitySampledImageArrayNonUniformIndexing);
state.builder.AddAnnotation(spv::OpDecorate, id, spv::DecorationNonUniform);
}
} // namespace
uint32_t LoadImageDescriptor(EmitterState& state, uint32_t resource, uint32_t mip,
uint32_t array_index) {
const auto pointer = ImageDescriptorPointer(state, resource, mip, array_index);
@@ -145,29 +154,28 @@ uint32_t LoadImageDescriptor(EmitterState& state, uint32_t resource, uint32_t mi
state.builder.AddFunction(spv::OpLoad, state.images[IR::ImageBindingIndex(kind)].type,
image, pointer);
if (array_index != 0u) {
state.builder.RequireExtension("SPV_EXT_descriptor_indexing");
state.builder.RequireCapability(spv::CapabilityShaderNonUniform);
state.builder.RequireCapability(spv::CapabilitySampledImageArrayNonUniformIndexing);
state.builder.AddAnnotation(spv::OpDecorate, image, spv::DecorationNonUniform);
DecorateNonUniformDescriptor(state, image);
}
return image;
}
uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler) {
uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler, uint32_t array_index) {
if (sampler >= state.program.info.samplers.size()) {
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::Samplers, sampler,
"sampler resource index is out of range");
}
const auto pointer = DescriptorElementPointer(
state, state.sampler_pointer_type, state.sampler_variable, ConstantU32(state, sampler),
state, state.sampler_pointer_type, state.sampler_variable,
array_index == 0u ? ConstantU32(state, sampler) : array_index,
IR::DescriptorBindingKind::Samplers, sampler, "sampler descriptor array was not emitted");
const auto sampler_id = state.builder.AllocateId();
state.builder.AddFunction(spv::OpLoad, state.sampler_type, sampler_id, pointer);
if (array_index != 0u) DecorateNonUniformDescriptor(state, sampler_id);
return sampler_id;
}
uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampler_id, uint32_t mip,
uint32_t array_index) {
uint32_t array_index, bool sampler_dynamic) {
const auto& image_resource = state.program.info.images.at(resource);
EXIT_IF(image_resource.resource_class != IR::ImageResourceClass::Sampled);
const auto image = LoadImageDescriptor(state, resource, mip, array_index);
@@ -179,8 +187,8 @@ uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampl
spv::OpTypeSampledImage, state.images[IR::ImageBindingIndex(kind)].type);
}
state.builder.AddFunction(spv::OpSampledImage, sampled_type, sampled_image, image, sampler_id);
if (array_index != 0u) {
state.builder.AddAnnotation(spv::OpDecorate, sampled_image, spv::DecorationNonUniform);
if (array_index != 0u || sampler_dynamic) {
DecorateNonUniformDescriptor(state, sampled_image);
}
return sampled_image;
}
@@ -898,14 +898,25 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
operands[1] = AddressF32(ctx, mem, *address, layout.bias);
operand_count = 2;
}
const auto sampler_id = LoadSamplerDescriptor(state, mem.sampler);
const auto& samplers = state.program.info.samplers.at(mem.sampler).indirect_resources;
uint32_t sampler_index = 0;
if (!samplers.empty()) {
const auto* handle = inst.Arg(1).ResolveInstruction();
if (handle == nullptr || handle->GetOpcode() != IR::ValueOpcode::GetSamplerResource)
ctx.Fail(inst, "has invalid finite sampler key provenance");
sampler_index = EmitIndexSwitch(
state, ctx.Def(handle->Arg(0)), static_cast<uint32_t>(samplers.size()),
TypeU32(state), [&](uint32_t ordinal) { return ConstantU32(state, samplers[ordinal]); });
}
const auto sampler_id = LoadSamplerDescriptor(state, mem.sampler, sampler_index);
const auto EmitSample = [&](uint32_t resource, uint32_t array_index) {
const auto& candidate = state.program.info.images[resource];
const auto coord =
CoordF32(ctx, mem, *address, layout.coord,
ImageDimensionInfoFor(candidate.dimension).coordinate_components,
candidate.cube);
const auto sampled = MakeSampledImage(state, resource, sampler_id, 0u, array_index);
const auto sampled = MakeSampledImage(state, resource, sampler_id, 0u, array_index,
sampler_index != 0u);
const auto sample = state.builder.AllocateId();
state.builder.AddFunction(opcode, result_type, sample, sampled, coord,
std::span(&dref_value, dref ? 1u : 0u),
@@ -322,10 +322,11 @@ uint32_t ImageDescriptorPointer(EmitterState& state, uint32_t resource, uint32_t
uint32_t LoadImageDescriptor(EmitterState& state, uint32_t resource, uint32_t mip = 0,
uint32_t array_index = 0);
uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler);
uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler, uint32_t array_index = 0);
uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampler_id,
uint32_t mip = 0, uint32_t array_index = 0);
uint32_t mip = 0, uint32_t array_index = 0,
bool sampler_dynamic = false);
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
uint32_t texel);