Merge pull request #1418 from gjonn/feat/agc-unnormalized-implicit-lod
feat(agc): sample the base level through unnormalized samplers
This commit is contained in:
commit
8bcd97cd5b
|
|
@ -2634,9 +2634,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());
|
||||
}
|
||||
|
||||
|
|
@ -2646,7 +2646,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");
|
||||
|
|
@ -2721,6 +2720,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{};
|
||||
|
|
@ -2734,7 +2741,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