fix(shader): fold texel offsets into base-level unnormalized samples

An implicit-LOD or gradient sample with a texel offset through an
unnormalized S# was refused at bind time with "unnormalized guest sampler
is used by an implicit-LOD sample". PPSA01317 aborts on boot on such a
pixel image_sample_o. The base-level path already samples these through
OpImageSampleExplicitLod with LOD 0, which Vulkan allows for unnormalized
samplers, but it dropped the offset. It now adds the offset to the
coordinates in that branch, as the explicit-LOD offset fold does, so a
sampler whose uses also include offsets takes the base-level path. A
normalized S# keeps the offset operand.

AI-assisted: yes
This commit is contained in:
Gjon Gojcevic committed 2026-10-17 14:43:23 -04:00
1 parent f219248f0b
commit 7c19c66def
3 files changed
+22 -2

No files matched your search

@@ -4272,6 +4272,26 @@ void verifyUnnormalizedSamplers() {
pixel.context.userData = normalizedPixelData;
const auto pixelNormalized = Recompile(pixel);
require(proven(pixelNormalized, false) && implicitSamples(pixelNormalized.spirv.Words()) == 1u, "unnormalized samplers: a pixel image_sample through a normalized S# is not an implicit-LOD sample");
const std::array<std::uint32_t, 7> pixelOffsetCode{0x7e0202ffu, 0x00000201u, 0xf0c00f08u, 0x00400801u, 0xf800180fu, 0x0b0a0908u, 0xbf810000u};
pixel.shader = {ShaderStage::Fragment, 0x50000u, pixelOffsetCode, 0, {}};
pixel.context.userData = pixelData;
pixel.target.supportedCapabilities = capabilities;
pixel.target.nonConstantImageOffsets = true;
const auto pixelOffsetUnnormalized = Recompile(pixel);
require(proven(pixelOffsetUnnormalized, true), "unnormalized texel offsets: a pixel image_sample_o through an unnormalized S# was not flagged");
auditSpirv(pixelOffsetUnnormalized.spirv.Words());
require(foldedSamples(pixelOffsetUnnormalized.spirv.Words()), "unnormalized texel offsets: a pixel image_sample_o offset was not added to the base-level coordinates");
pixel.context.userData = normalizedPixelData;
const auto pixelOffsetNormalized = Recompile(pixel);
std::vector<std::uint32_t> implicitMasks;
const auto offsetWords = pixelOffsetNormalized.spirv.Words();
for (std::size_t cursor = 5; cursor < offsetWords.size();) {
const auto count = offsetWords[cursor] >> 16u;
require(count != 0 && count <= offsetWords.size() - cursor, "unnormalized texel offsets: truncated SPIR-V instruction");
if ((offsetWords[cursor] & 0xffffu) == spv::OpImageSampleImplicitLod && count > 5u) implicitMasks.push_back(offsetWords[cursor + 5]);
cursor += count;
}
require(proven(pixelOffsetNormalized, false) && implicitMasks.size() == 1u && (implicitMasks[0] & (spv::ImageOperandsConstOffsetMask | spv::ImageOperandsOffsetMask)) != 0u, "unnormalized texel offsets: a normalized pixel image_sample_o is not an implicit-LOD sample with its offset");
}
void verifyUnusedUnnormalizedSampler() {
@@ -140,7 +140,7 @@ inline bool FoldsTexelOffsets(const SamplerResource& sampler) {
}
inline bool SamplesBaseLevelIfUnnormalized(const SamplerResource& sampler) {
return (sampler.uses & (SamplerUseImplicitLod | SamplerUseGradient)) != 0u && (sampler.uses & ~static_cast<std::uint32_t>(SamplerUseExplicitLod | SamplerUseImplicitLod | SamplerUseGradient | SamplerUseNonZeroLod)) == 0u && !sampler.depthCompare;
return (sampler.uses & (SamplerUseImplicitLod | SamplerUseGradient)) != 0u && (sampler.uses & ~static_cast<std::uint32_t>(SamplerUseExplicitLod | SamplerUseImplicitLod | SamplerUseGradient | SamplerUseNonZeroLod | SamplerUseOffset)) == 0u && !sampler.depthCompare;
}
struct SampledResourcePair {
@@ -1465,7 +1465,7 @@ void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, con
const auto selector = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::UnnormalizedBaseLevelBase + mem.sampler, 0u);
const auto unnormalized = Binary(state, spv::OpINotEqual, TypeBool(state), selector, ConstantU32(state, 0u));
sample = EmitValueIfElse(state, unnormalized, resultType, [&] {
return emitSample(setup.coord, spv::ImageOperandsLodMask, {ZeroF32(state)}, spv::OpImageSampleExplicitLod);
return emitSample(hasOffset ? FoldedOffsetCoord(ctx, access, setup) : setup.coord, spv::ImageOperandsLodMask, {ZeroF32(state)}, spv::OpImageSampleExplicitLod);
}, [&] {
return emitSample(setup.coord, operandMask, operands, opcode);
});