feat(agc): sample the base level through unnormalized samplers
The hardware ignores the LOD for unnormalized coordinates and samples the base level. A sampler used only by explicit-LOD, implicit-LOD, bias and gradient samples gets a pipeline specialization constant set from its unnormalized bit when the binding plan is prepared. When the constant is set, each of its samples is emitted as OpImageSampleExplicitLod with LOD 0, which is what Vulkan requires of unnormalized samplers. Otherwise the sample keeps its own form. Such shaders are no longer refused.
This commit is contained in:
parent
c13dc7e6b4
commit
63ed0ffeaf
|
|
@ -2456,9 +2456,9 @@ void verifyUnnormalizedSamplers() {
|
|||
require(proven(plain, false), "unnormalized samplers: a normalized S# was flagged");
|
||||
require(plain.variantId == accepted.variantId && plain.spirv.Words() == accepted.spirv.Words(), "unnormalized samplers: the unnormalized S# compiled another variant");
|
||||
|
||||
for (const std::uint32_t mimg : {0xf0900f08u, 0xf0800f08u, 0xf0940f08u, 0xf0840f08u}) {
|
||||
for (const std::uint32_t mimg : {0xf0900f08u, 0xf0800f08u, 0xf0940f08u, 0xf0840f08u, 0xf0880f08u}) {
|
||||
const auto result = recompile(program(mimg), userData(unnormalized));
|
||||
require(proven(result, true), "unnormalized samplers: a compute image_sample_l, image_sample, image_sample_b or image_sample_cl was not flagged");
|
||||
require(proven(result, true), "unnormalized samplers: a compute image_sample_l, image_sample, image_sample_b, image_sample_cl or image_sample_d was not flagged");
|
||||
auditSpirv(result.spirv.Words());
|
||||
}
|
||||
|
||||
|
|
@ -2468,7 +2468,6 @@ void verifyUnnormalizedSamplers() {
|
|||
reject(0xf0bc0f08u, imageData(unnormalized, Type2D, Format32Float, 0u), "unnormalized guest sampler is used with depth comparison, which is not implemented", "unnormalized samplers: image_sample_c_lz was accepted");
|
||||
reject(0xf11c0108u, userData(unnormalized), "unnormalized guest sampler is used by a gather, which is not implemented", "unnormalized samplers: image_gather4_lz was accepted");
|
||||
reject(0xf1800308u, userData(unnormalized), "unnormalized guest sampler is used by image_get_lod, which is not implemented", "unnormalized samplers: image_get_lod was accepted");
|
||||
reject(0xf0880f08u, userData(unnormalized), "unnormalized guest sampler is used by a sample with derivatives, which is not implemented", "unnormalized samplers: image_sample_d was accepted");
|
||||
reject(0xf0800f09u, userData(unnormalized), "unnormalized guest sampler is used by an image_sample_*_a variant, which is not implemented", "unnormalized samplers: image_sample_a was accepted");
|
||||
reject(0xf09c0f18u, imageData(unnormalized, TypeCube, Format8888UNorm, 5u), "unnormalized guest sampler samples a 1D-array, 2D-array, 3D, cube or multisampled image", "unnormalized samplers: a cube T# was accepted");
|
||||
reject(0xf09c0f10u, imageData(unnormalized, Type3D, Format8888UNorm, 3u), "unnormalized guest sampler samples a 1D-array, 2D-array, 3D, cube or multisampled image", "unnormalized samplers: a 3D T# was accepted");
|
||||
|
|
@ -2543,6 +2542,14 @@ void verifyUnnormalizedSamplers() {
|
|||
require(!masks.empty() && std::ranges::all_of(masks, [](std::uint32_t mask) { return (mask & spv::ImageOperandsOffsetMask) != 0u; }), "unnormalized texel offsets: a normalized computed offset lost its Offset operand");
|
||||
}
|
||||
|
||||
const auto gradient = program(0xf0880f08u);
|
||||
const auto gradientAddress = nextAddress += 0x1000u;
|
||||
const auto gradientUnnormalized = recompile(gradient, userData(unnormalized), gradientAddress);
|
||||
const auto gradientNormalized = recompile(gradient, userData(normalized), gradientAddress);
|
||||
require(proven(gradientNormalized, false) && gradientNormalized.variantId == gradientUnnormalized.variantId && gradientNormalized.PipelineVariantId() != gradientUnnormalized.PipelineVariantId(), "unnormalized samplers: a normalized image_sample_d did not get its own specialization of the shared artifact");
|
||||
require(sampleMasks(gradientNormalized.spirv.Words()) == std::vector<std::uint32_t>{spv::ImageOperandsGradMask}, "unnormalized samplers: a normalized image_sample_d lost its derivatives");
|
||||
require(sampleMasks(gradientUnnormalized.spirv.Words()) == std::vector<std::uint32_t>{spv::ImageOperandsLodMask}, "unnormalized samplers: an unnormalized image_sample_d kept its derivatives");
|
||||
|
||||
const std::array<std::uint32_t, 5> pixelCode{0xf0800f08u, 0x00400801u, 0xf800180fu, 0x0b0a0908u, 0xbf810000u};
|
||||
const auto pixelData = userData(unnormalized);
|
||||
RecompileRequest pixel{};
|
||||
|
|
@ -2556,7 +2563,23 @@ void verifyUnnormalizedSamplers() {
|
|||
pixel.target.subgroupSize = 64;
|
||||
pixel.layout.pushConstantSizeBytes = 128;
|
||||
pixel.useCache = false;
|
||||
expectFailure([&] { static_cast<void>(Recompile(pixel)); }, "unnormalized guest sampler is used by an implicit-LOD sample, which is not implemented", "unnormalized samplers: a pixel image_sample was accepted");
|
||||
const auto implicitSamples = [](const std::vector<std::uint32_t>& words) {
|
||||
std::size_t samples = 0;
|
||||
for (std::size_t cursor = 5; cursor < words.size();) {
|
||||
const auto count = words[cursor] >> 16u;
|
||||
require(count != 0 && count <= words.size() - cursor, "unnormalized samplers: truncated SPIR-V instruction");
|
||||
if ((words[cursor] & 0xffffu) == spv::OpImageSampleImplicitLod) ++samples;
|
||||
cursor += count;
|
||||
}
|
||||
return samples;
|
||||
};
|
||||
const auto pixelUnnormalized = Recompile(pixel);
|
||||
require(proven(pixelUnnormalized, true), "unnormalized samplers: a pixel image_sample through an unnormalized S# was not flagged");
|
||||
auditSpirv(pixelUnnormalized.spirv.Words());
|
||||
const auto normalizedPixelData = userData(normalized);
|
||||
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");
|
||||
}
|
||||
|
||||
void verifyUnusedUnnormalizedSampler() {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@ alignas(256) constexpr std::array<std::uint32_t, 18> LevelZeroCode{
|
|||
0x80000b01, 0xbf810000,
|
||||
};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 22> GradientCode{
|
||||
0x34020086, 0xe0301000, 0x80000601, 0xe0301004, 0x80000701, 0xbf8c3f70, 0x7e0402f6, 0x7e060280,
|
||||
0x7e080280, 0x7e0a02f6, 0xf0880f08, 0x00610802, 0xbf8c3f70, 0xe0701010, 0x80000801, 0xe0701014,
|
||||
0x80000901, 0xe0701018, 0x80000a01, 0xe070101c, 0x80000b01, 0xbf810000,
|
||||
};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 45> OffsetCode{
|
||||
0x34020086, 0xe0301008, 0x80000201, 0xe0301000, 0x80000301, 0xe0301004, 0x80000401, 0xbf8c3f70,
|
||||
0x7e0a02ff, 0x402ccccd, 0x7e2802ff, 0x0000023d, 0xf0dc0f08, 0x00610802, 0xf0d00f08, 0x00610c02,
|
||||
|
|
@ -75,6 +81,7 @@ struct Program {
|
|||
|
||||
const Program Plain{Code, {"image_sample_lz", "image_sample_l 2.7", "image_sample"}, {Offset::None, Offset::None, Offset::None}};
|
||||
const Program LevelZero{LevelZeroCode, {"image_sample_lz", "", ""}, {Offset::None, Offset::None, Offset::None}, 1};
|
||||
const Program Gradient{GradientCode, {"image_sample_d (4, 0, 0, 4)", "", ""}, {Offset::None, Offset::None, Offset::None}, 1};
|
||||
const Program Offsets{OffsetCode, {"image_sample_lz_o", "image_sample_l_o 2.7", "image_sample_lz_o (-3, 2) NSA"}, {Offset::Lane, Offset::Lane, Offset::Constant}};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 34> ConstantCode{
|
||||
|
|
@ -313,6 +320,8 @@ int main() {
|
|||
Check(Plain, sampler, 0u, "first level of a 4-level image", coordinates, multiSamples);
|
||||
Require(multiSamples == singleSamples, std::string(sampler.name) + ": the first level of a 4-level image does not sample like a 1-level image");
|
||||
Check(Offsets, sampler, 0u, "1-level image", offsetCoordinates, Run(*device, Offsets, single, sampler.words, offsetCoordinates));
|
||||
Check(Gradient, sampler, 0u, "1-level image", coordinates, Run(*device, Gradient, single, sampler.words, coordinates));
|
||||
Check(Gradient, sampler, 0u, "first level of a 4-level image", coordinates, Run(*device, Gradient, multi, sampler.words, coordinates));
|
||||
if (!sampler.atBaseLevel) continue;
|
||||
const auto levelZeroSingle = Run(*device, LevelZero, single, sampler.words, coordinates);
|
||||
const auto levelZeroChain = Run(*device, LevelZero, chain, sampler.words, coordinates);
|
||||
|
|
@ -324,6 +333,7 @@ int main() {
|
|||
ExpectFailure(*device, Plain, chain, Samplers[0].words, "single-level", "a 4-level view read by image_sample_l and image_sample");
|
||||
ExpectFailure(*device, Plain, fromLevelOne, Samplers[0].words, "single-level", "a view starting at mip 1 read by image_sample_l and image_sample");
|
||||
ExpectFailure(*device, Offsets, chain, Samplers[0].words, "single-level", "a 4-level view read with texel offsets");
|
||||
ExpectFailure(*device, Gradient, chain, Samplers[0].words, "single-level", "a 4-level view read by image_sample_d");
|
||||
ExpectFailure(*device, LevelZero, chain, Samplers[2].words, "single-level", "a 4-level view through an S# with a minimum LOD and a LOD bias");
|
||||
ExpectFailure(*device, Plain, single, {0x00008092u, 0x00fff000u, 0x05100000u, 0u}, "different minification", "unequal minification and magnification filters");
|
||||
ExpectFailure(*device, Plain, single, {0x00008090u, 0x00fff000u, 0x05500000u, 0u}, "clamp mode 0", "wrap on X");
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ inline bool FoldsTexelOffsets(const SamplerResource& sampler) {
|
|||
return (sampler.uses & SamplerUseOffset) != 0u && (sampler.uses & ~static_cast<std::uint32_t>(SamplerUseExplicitLod | SamplerUseOffset | SamplerUseNonZeroLod)) == 0u && !sampler.depthCompare;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct SampledResourcePair {
|
||||
std::uint32_t image = 0;
|
||||
std::uint32_t sampler = 0;
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ UnnormalizedProof ProveUnnormalized(const ShaderInfo& info, const ResourceSnapsh
|
|||
continue;
|
||||
}
|
||||
const std::uint32_t unsupported = sampler.uses & ~static_cast<std::uint32_t>(SamplerUseExplicitLod | SamplerUseOffset | SamplerUseNonZeroLod);
|
||||
if (unsupported != 0u) {
|
||||
if (unsupported != 0u && !SamplesBaseLevelIfUnnormalized(sampler)) {
|
||||
failUnnormalized(UnnormalizedUseReason(unsupported));
|
||||
}
|
||||
if (sampler.depthCompare) {
|
||||
|
|
@ -259,6 +259,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
const auto unnormalized = ProveUnnormalized(info, snapshot);
|
||||
for (std::uint32_t index = 0; index < info.samplers.size(); ++index) {
|
||||
if (FoldsTexelOffsets(info.samplers[index])) plan.specialization.push_back({PipelineSpecialization::FoldOffsetBase + index, unnormalized.samplers[index] ? 1u : 0u});
|
||||
if (SamplesBaseLevelIfUnnormalized(info.samplers[index])) plan.specialization.push_back({PipelineSpecialization::UnnormalizedBaseLevelBase + index, unnormalized.samplers[index] ? 1u : 0u});
|
||||
}
|
||||
std::vector<std::uint32_t> compareStates(info.images.size());
|
||||
for (std::uint32_t index = 0; index < info.images.size(); ++index) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ inline constexpr std::uint32_t DescriptorIndexStride = 128u;
|
|||
inline constexpr std::uint32_t ImageModeBase = 32768u;
|
||||
inline constexpr std::uint32_t ImageModeStride = 256u;
|
||||
inline constexpr std::uint32_t FoldOffsetBase = 57344u;
|
||||
inline constexpr std::uint32_t UnnormalizedBaseLevelBase = 57408u;
|
||||
|
||||
inline constexpr std::uint32_t DescriptorIndex(std::uint32_t binding, std::uint32_t element) {
|
||||
return DescriptorIndexBase + binding * DescriptorIndexStride + element;
|
||||
|
|
|
|||
|
|
@ -1474,10 +1474,10 @@ void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, con
|
|||
operands.insert(operands.end() - ((operandMask & spv::ImageOperandsMinLodMask) != 0u ? 1 : 0), offset);
|
||||
|
||||
}
|
||||
const auto emitSample = [&](std::uint32_t coord, std::uint32_t mask, const std::vector<std::uint32_t>& arguments) {
|
||||
const auto emitSample = [&](std::uint32_t coord, std::uint32_t mask, const std::vector<std::uint32_t>& arguments, std::uint32_t sampleOpcode) {
|
||||
const auto sampled = MakeSampledImage(state, mem.resource, mem.sampler, access.slot);
|
||||
const auto sample = state.module.AllocateId();
|
||||
std::vector<std::uint32_t> words = {opcode, resultType, sample, sampled, coord};
|
||||
std::vector<std::uint32_t> words = {sampleOpcode, resultType, sample, sampled, coord};
|
||||
if (setup.dref) {
|
||||
words.push_back(drefValue);
|
||||
}
|
||||
|
|
@ -1489,10 +1489,19 @@ void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, con
|
|||
return sample;
|
||||
};
|
||||
std::uint32_t sample = 0;
|
||||
if (!foldOffset) {
|
||||
sample = emitSample(setup.coord, operandMask, operands);
|
||||
const bool baseLevelIfUnnormalized = !setup.dref && SamplesBaseLevelIfUnnormalized(state.program.Info().samplers.at(mem.sampler));
|
||||
if (baseLevelIfUnnormalized) {
|
||||
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(setup.coord, operandMask, operands, opcode);
|
||||
});
|
||||
} else if (!foldOffset) {
|
||||
sample = emitSample(setup.coord, operandMask, operands, opcode);
|
||||
} else if (!offsetOperand) {
|
||||
sample = emitSample(FoldedOffsetCoord(ctx, access, setup), foldedMask, foldedOperands);
|
||||
sample = emitSample(FoldedOffsetCoord(ctx, access, setup), foldedMask, foldedOperands, opcode);
|
||||
} else {
|
||||
const auto selector = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::FoldOffsetBase + mem.sampler, 0u);
|
||||
const auto folded = Binary(state, spv::OpINotEqual, TypeBool(state), selector, ConstantU32(state, 0u));
|
||||
|
|
@ -1502,11 +1511,11 @@ void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, con
|
|||
state.module.AddFunction(spv::OpSelectionMerge, merge, spv::SelectionControlMaskNone);
|
||||
state.module.AddFunction(spv::OpBranchConditional, folded, foldLabel, operandLabel);
|
||||
EmitLabel(state, foldLabel);
|
||||
const auto foldSample = emitSample(FoldedOffsetCoord(ctx, access, setup), foldedMask, foldedOperands);
|
||||
const auto foldSample = emitSample(FoldedOffsetCoord(ctx, access, setup), foldedMask, foldedOperands, opcode);
|
||||
const auto foldEnd = state.currentLabel;
|
||||
state.module.AddFunction(spv::OpBranch, merge);
|
||||
EmitLabel(state, operandLabel);
|
||||
const auto operandSample = emitSample(setup.coord, operandMask, operands);
|
||||
const auto operandSample = emitSample(setup.coord, operandMask, operands, opcode);
|
||||
const auto operandEnd = state.currentLabel;
|
||||
state.module.AddFunction(spv::OpBranch, merge);
|
||||
EmitLabel(state, merge);
|
||||
|
|
|
|||
Loading…
Reference in New Issue