Merge pull request #1417 from gjonn/feat/agc-fold-constant-swizzle-samples
feat(agc): fold samples of views whose channels all select constants
This commit is contained in:
commit
5a014b888a
|
|
@ -1564,6 +1564,11 @@ void verifyUnusedUnnormalizedSampler() {
|
|||
auto selected = info;
|
||||
selected.images[0].indirectRoot = 0u;
|
||||
expectFailure([&] { static_cast<void>(populate(selected)); }, "unnormalized guest sampler samples an image selected at run time, which is not implemented", "unnormalized samplers: an image table root was accepted");
|
||||
snapshot.images[0].dwords[3] = 0xa0000041u;
|
||||
const auto constant = populate(info);
|
||||
require(constant.size() == 1u && constant[0].samplerUnnormalized == std::vector<bool>{true}, "unnormalized samplers: a 3D view whose channels select constants was bound or refused");
|
||||
expectFailure([&] { static_cast<void>(populate(selected)); }, "unnormalized guest sampler samples an image selected at run time, which is not implemented", "unnormalized samplers: an image table root whose channels select constants was accepted");
|
||||
snapshot.images[0].dwords[3] = 0x90000facu;
|
||||
auto compared = info;
|
||||
compared.samplers[0].depthCompare = true;
|
||||
expectFailure([&] { static_cast<void>(populate(compared)); }, "unnormalized guest sampler is used with depth comparison, which is not implemented", "unnormalized samplers: a depth-compare S# without live uses was accepted");
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ constexpr std::int32_t Width = 13;
|
|||
constexpr std::int32_t Height = 7;
|
||||
constexpr std::uint32_t StorageLevels = 4;
|
||||
constexpr std::uint32_t Format8888UNorm = 56;
|
||||
constexpr std::uint32_t Format32UInt = 20;
|
||||
constexpr std::uint32_t Format32SInt = 21;
|
||||
constexpr std::uint32_t Type1D = 8;
|
||||
constexpr std::uint32_t Type2D = 9;
|
||||
constexpr std::uint32_t Type3D = 10;
|
||||
constexpr std::uint32_t OneZeroOneZero = 0x041u;
|
||||
constexpr std::uint8_t OtherLevelTexel = 0x40u;
|
||||
alignas(256) std::array<std::uint32_t, Threads * Words> Buffer{};
|
||||
alignas(256) std::array<std::uint8_t, 16384> SingleLevel{};
|
||||
|
|
@ -44,6 +49,14 @@ alignas(256) constexpr std::array<std::uint32_t, 40> Code{
|
|||
|
||||
constexpr std::array<const char*, 3> Instructions{"image_sample_lz", "image_sample_l 2.7", "image_sample"};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 34> ConstantCode{
|
||||
0x34020086, 0xe0301000, 0x80000201, 0xe0301004, 0x80000301, 0xbf8c3f70, 0xf09c0608, 0x00610802,
|
||||
0xf11c0208, 0x00610a02, 0xf11c0408, 0x00610e02, 0xbf8c3f70, 0xe0701010, 0x80000801, 0xe0701014,
|
||||
0x80000901, 0xe0701018, 0x80000a01, 0xe070101c, 0x80000b01, 0xe0701020, 0x80000c01, 0xe0701024,
|
||||
0x80000d01, 0xe0701028, 0x80000e01, 0xe070102c, 0x80000f01, 0xe0701030, 0x80001001, 0xe0701034,
|
||||
0x80001101, 0xbf810000,
|
||||
};
|
||||
|
||||
struct SamplerCase {
|
||||
const char* name;
|
||||
std::array<std::uint32_t, 4> words;
|
||||
|
|
@ -107,6 +120,12 @@ void FillTexture(std::span<std::uint8_t> texels, std::uint32_t levels) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string Hex(std::uint32_t value) {
|
||||
char text[16];
|
||||
std::snprintf(text, sizeof(text), "0x%08x", value);
|
||||
return text;
|
||||
}
|
||||
|
||||
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(data);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x31016facu};
|
||||
|
|
@ -128,7 +147,7 @@ std::array<std::uint32_t, 8> TextureDescriptor(const void* data, std::uint32_t l
|
|||
|
||||
using Samples = std::vector<std::array<std::uint32_t, Results>>;
|
||||
|
||||
Samples Run(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>& texture, const std::array<std::uint32_t, 4>& sampler, const std::vector<Coordinate>& coordinates) {
|
||||
Samples Run(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>& texture, const std::array<std::uint32_t, 4>& sampler, const std::vector<Coordinate>& coordinates, std::span<const std::uint32_t> code = Code) {
|
||||
Samples samples;
|
||||
for (std::size_t first = 0; first < coordinates.size(); first += Threads) {
|
||||
Buffer.fill(0xdeadbeefu);
|
||||
|
|
@ -142,7 +161,6 @@ Samples Run(AgcDriver::VulkanDevice& device, const std::array<std::uint32_t, 8>&
|
|||
std::copy(buffer.begin(), buffer.end(), userData.begin());
|
||||
std::copy(texture.begin(), texture.end(), userData.begin() + 4);
|
||||
std::copy(sampler.begin(), sampler.end(), userData.begin() + 12);
|
||||
const std::span<const std::uint32_t> code(Code);
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1};
|
||||
ShaderRecompiler::RecompileRequest request{
|
||||
|
|
@ -231,6 +249,43 @@ int main() {
|
|||
ExpectFailure(*device, TextureDescriptor(MultiLevel.data(), 2u, 2u), Samplers[0].words, "single-level", "a 3-level view");
|
||||
ExpectFailure(*device, single, {0x00008092u, 0x00fff000u, 0x05100000u, 0u}, "different minification", "unequal minification and magnification filters");
|
||||
ExpectFailure(*device, single, {0x00008090u, 0x00fff000u, 0x05500000u, 0u}, "clamp mode 0", "wrap on X");
|
||||
const auto constantView = [&](std::uint32_t type, std::uint32_t format, std::uint32_t swizzle) {
|
||||
auto descriptor = TextureDescriptor(SingleLevel.data(), 0u, 0u);
|
||||
descriptor[1] = (descriptor[1] & ~(0x1ffu << 20u)) | (format << 20u);
|
||||
descriptor[3] = swizzle | (type << 28u);
|
||||
return descriptor;
|
||||
};
|
||||
for (const auto& sampler : Samplers) {
|
||||
for (const auto& sample : Run(*device, constantView(Type3D, Format8888UNorm, OneZeroOneZero), sampler.words, coordinates)) {
|
||||
for (std::uint32_t index = 0; index < Results; ++index) {
|
||||
const std::uint32_t expected = index % 2u == 0u ? 0x3f800000u : 0u;
|
||||
Require(sample[index] == expected, std::string(sampler.name) + ": a 3D view whose channels select (1, 0, 1, 0) returned " + Hex(sample[index]) + " for result " + std::to_string(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
struct ConstantCase {
|
||||
const char* name;
|
||||
std::array<std::uint32_t, 8> texture;
|
||||
std::uint32_t one;
|
||||
};
|
||||
const std::array<ConstantCase, 4> constants{{
|
||||
{"a 3D UNORM view selecting (1, 0, 1, 0)", constantView(Type3D, Format8888UNorm, OneZeroOneZero), 0x3f800000u},
|
||||
{"a 32_UINT view selecting (1, 0, 1, 0)", constantView(Type2D, Format32UInt, OneZeroOneZero), 1u},
|
||||
{"a 32_SINT view selecting (1, 0, 1, 0)", constantView(Type2D, Format32SInt, OneZeroOneZero), 1u},
|
||||
{"a 1D view selecting (0, 0, 0, 0)", constantView(Type1D, Format8888UNorm, 0u), 0u},
|
||||
}};
|
||||
const std::array<const char*, 3> constantInstructions{"image_sample_lz dmask 0x6", "image_gather4_lz dmask 0x2", "image_gather4_lz dmask 0x4"};
|
||||
for (const auto& constant : constants) {
|
||||
auto normalized = Samplers[0].words;
|
||||
normalized[0] &= ~0x8000u;
|
||||
for (const auto& sample : Run(*device, constant.texture, normalized, coordinates, ConstantCode)) {
|
||||
const std::array<std::uint32_t, 10> expected{0u, constant.one, 0u, 0u, 0u, 0u, constant.one, constant.one, constant.one, constant.one};
|
||||
for (std::uint32_t index = 0; index < expected.size(); ++index) {
|
||||
const auto instruction = constantInstructions[index < 2u ? 0u : index < 6u ? 1u : 2u];
|
||||
Require(sample[index] == expected[index], std::string(instruction) + ", " + constant.name + ": result " + std::to_string(index) + " is " + Hex(sample[index]) + ", expected " + Hex(expected[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::puts("image sample unnormalized tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ struct ImageResource {
|
|||
bool packed = false;
|
||||
bool fmaskCompatible = true;
|
||||
bool depthBitsCompatible = true;
|
||||
bool constantSwizzle = false;
|
||||
bool constantSwizzleCompatible = true;
|
||||
std::uint32_t byElements = 0;
|
||||
std::uint32_t byComponents = 0;
|
||||
IrBufferFormat packedFormat = IrBufferFormat::Invalid;
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ UnnormalizedProof ProveUnnormalized(const ShaderInfo& info, const ResourceSnapsh
|
|||
if (image.indirectRoot != ImageResource::NoIndirectImage) {
|
||||
failUnnormalized("samples an image selected at run time");
|
||||
}
|
||||
if (image.constantSwizzle) {
|
||||
continue;
|
||||
}
|
||||
if ((image.dimension != RdnaImageDimension::Dim1D && image.dimension != RdnaImageDimension::Dim2D) || image.cube) {
|
||||
failUnnormalized("samples a 1D-array, 2D-array, 3D, cube or multisampled image");
|
||||
}
|
||||
|
|
@ -295,7 +298,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
for (std::uint32_t component = 0; component < 4u; ++component) plan.specialization.push_back({PipelineSpecialization::ExportBase + target * 4u + component, (exportMappings[target] >> (component * 2u)) & 3u});
|
||||
}
|
||||
std::vector<std::uint32_t> samplerModes(info.samplers.size(), 0u);
|
||||
const auto samplerMode = [](const ImageResource& image) { return image.numericClass == IrTextureNumericClass::Sint || image.conversionFormat != IrBufferFormat::Invalid || image.depthBits ? 2u : 1u; };
|
||||
const auto samplerMode = [](const ImageResource& image) { return !image.constantSwizzle && (image.numericClass == IrTextureNumericClass::Sint || image.conversionFormat != IrBufferFormat::Invalid || image.depthBits) ? 2u : 1u; };
|
||||
for (const auto& pair : info.sampledPairs) {
|
||||
const auto& image = info.images.at(pair.image);
|
||||
if (image.indirectRoot == ImageResource::NoIndirectImage) samplerModes.at(pair.sampler) |= samplerMode(info.runtimeImageModes.at(pair.image).at(imageModes[pair.image]));
|
||||
|
|
@ -343,7 +346,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
previous = resource;
|
||||
{
|
||||
const auto word = snapshot.images.at(resource).dwords[3];
|
||||
active = DescriptorBindingForImage(mode) == logical.kind && mode.packedFormat != IrBufferFormat::Fmask8_S2_F1 && (image.mipMode != ImageMipMode::DynamicStorage || mip <= ((word >> 16u) & 0xfu) - ((word >> 12u) & 0xfu));
|
||||
active = DescriptorBindingForImage(mode) == logical.kind && mode.packedFormat != IrBufferFormat::Fmask8_S2_F1 && !mode.constantSwizzle && (image.mipMode != ImageMipMode::DynamicStorage || mip <= ((word >> 16u) & 0xfu) - ((word >> 12u) & 0xfu));
|
||||
}
|
||||
} else if (samplerHeap) {
|
||||
active = (samplerModes.at(resource) & (1u << (element & 1u))) != 0u;
|
||||
|
|
|
|||
|
|
@ -703,6 +703,21 @@ std::vector<ImageResource> ResourceMaterializer::RuntimeImageModes(const ImageRe
|
|||
modes.push_back(mode);
|
||||
}
|
||||
}
|
||||
if (image.constantSwizzleCompatible && !storage && !image.depthCompare && !image.packed && image.indirectRoot == ImageResource::NoIndirectImage) {
|
||||
for (const auto numeric : {IrTextureNumericClass::Float, IrTextureNumericClass::Uint, IrTextureNumericClass::Sint}) {
|
||||
auto mode = image;
|
||||
mode.numericClass = numeric;
|
||||
mode.conversionFormat = IrBufferFormat::Invalid;
|
||||
mode.packedFormat = IrBufferFormat::Invalid;
|
||||
mode.depthBits = false;
|
||||
mode.depthUnorm16 = false;
|
||||
mode.cube = false;
|
||||
mode.mipCount = 1u;
|
||||
mode.shaderSwizzle = ShaderImageIdentitySwizzle;
|
||||
mode.constantSwizzle = true;
|
||||
modes.push_back(mode);
|
||||
}
|
||||
}
|
||||
if (modes.empty()) throw std::runtime_error("image instruction has no supported runtime modes");
|
||||
return modes;
|
||||
}
|
||||
|
|
@ -727,6 +742,11 @@ std::uint32_t ResourceMaterializer::RuntimeImageMode(const ImageResource& image,
|
|||
}
|
||||
}
|
||||
if (decoded.mipCount > (image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u)) throw std::runtime_error("runtime storage image mip capacity exceeded");
|
||||
if ((descriptorImageSwizzle(descriptor) & 06666u) == 0u && !decoded.fmask && !decoded.depthBits && decoded.conversionFormat == IrBufferFormat::Invalid && !decoded.srgbDecode) {
|
||||
for (std::uint32_t index = 0u; index < modes.size(); ++index) {
|
||||
if (modes[index].constantSwizzle && modes[index].numericClass == decoded.numericClass) return index;
|
||||
}
|
||||
}
|
||||
for (std::uint32_t index = 0u; index < modes.size(); ++index) {
|
||||
const auto& mode = modes[index];
|
||||
if (((mode.emulatedCompare & EmulatedCompare::Enabled) != 0u) != emulated) continue;
|
||||
|
|
|
|||
|
|
@ -1061,6 +1061,7 @@ private:
|
|||
image.srgbDecodeCompatible = image.srgbDecodeCompatible && !ImageOpcodeInfoOf(op).needsSampler;
|
||||
image.fmaskCompatible = image.fmaskCompatible && op == IrOpcode::ImageRead && memory.dataBits == 32u;
|
||||
image.depthBitsCompatible = image.depthBitsCompatible && memory.dataBits == 32u;
|
||||
image.constantSwizzleCompatible = image.constantSwizzleCompatible && (op == IrOpcode::ImageSampleRaw || op == IrOpcode::ImageGatherRaw);
|
||||
if ((memory.imageSampleFlags & RdnaImageSampleFlagCompare) != 0u) {
|
||||
constexpr auto unsupported = RdnaImageSampleFlagLod | RdnaImageSampleFlagDerivative;
|
||||
if (op == IrOpcode::ImageGatherRaw || (memory.imageSampleFlags & unsupported) != 0u) image.emulatedCompare |= EmulatedCompare::Unsupported;
|
||||
|
|
|
|||
|
|
@ -474,6 +474,8 @@ void encodeInfo(Writer& writer, const CompiledShaderInfo& compiled) {
|
|||
out.Value(image.packed);
|
||||
out.Value(image.fmaskCompatible);
|
||||
out.Value(image.depthBitsCompatible);
|
||||
out.Value(image.constantSwizzle);
|
||||
out.Value(image.constantSwizzleCompatible);
|
||||
out.Value(image.byElements);
|
||||
out.Value(image.byComponents);
|
||||
out.Value(image.packedFormat);
|
||||
|
|
@ -550,7 +552,7 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
|
|||
in.Value(buffer.scalar);
|
||||
in.Value(buffer.typedAlignment);
|
||||
});
|
||||
reader.List(info.images, 72, [](Reader& in, ImageResource& image) {
|
||||
reader.List(info.images, 74, [](Reader& in, ImageResource& image) {
|
||||
in.Value(image.source);
|
||||
in.Value(image.firstUsePc);
|
||||
in.Value(image.resourceClass);
|
||||
|
|
@ -575,6 +577,8 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) {
|
|||
in.Value(image.packed);
|
||||
in.Value(image.fmaskCompatible);
|
||||
in.Value(image.depthBitsCompatible);
|
||||
in.Value(image.constantSwizzle);
|
||||
in.Value(image.constantSwizzleCompatible);
|
||||
in.Value(image.byElements);
|
||||
in.Value(image.byComponents);
|
||||
in.Value(image.packedFormat);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace ShaderRecompiler::ShaderDiskCache {
|
||||
|
||||
inline constexpr std::uint32_t FormatVersion = 21;
|
||||
inline constexpr std::uint32_t FormatVersion = 22;
|
||||
|
||||
enum class LoadStatus {
|
||||
Loaded,
|
||||
|
|
|
|||
|
|
@ -1470,7 +1470,28 @@ TableSelection TableSlot(SpirvValueEmitContext& ctx, const IrValue& inst, const
|
|||
return EmitIndirectImageSelector(ctx, image, key);
|
||||
}
|
||||
|
||||
void EmitConstantSwizzleSample(SpirvValueEmitContext& ctx, const ImageEmitAccess& access) {
|
||||
auto& state = ctx.state;
|
||||
const auto numericClass = access.image.numericClass;
|
||||
const bool gather = access.inst.Opcode() == IrOpcode::ImageGatherRaw;
|
||||
const auto scalarType = ImageScalarType(state, numericClass);
|
||||
const auto one = numericClass == IrTextureNumericClass::Float ? ConstantF32(state, 0x3f800000u) : numericClass == IrTextureNumericClass::Sint ? ConstantI32(state, 1) : ConstantU32(state, 1u);
|
||||
const auto zero = SampledComponentZero(state, numericClass);
|
||||
std::uint32_t channels[4] = {};
|
||||
for (std::uint32_t channel = 0; channel < 4u; channel++) {
|
||||
const auto selector = RuntimeImageSwizzle(state, access.mem.resource, gather ? ImageGatherComponent(EffectiveDmask(access.mem)) : channel);
|
||||
channels[channel] = Select(state, scalarType, Binary(state, spv::OpIEqual, TypeBool(state), selector, ConstantU32(state, 1u)), one, zero);
|
||||
}
|
||||
const auto value = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpCompositeConstruct, ImageVectorType(state, numericClass, 4), value, channels[0], channels[1], channels[2], channels[3]);
|
||||
ctx.Define(access.inst, ResultVector(ctx, access, value, numericClass, false, gather));
|
||||
}
|
||||
|
||||
void EmitSamplingOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access) {
|
||||
if (access.image.constantSwizzle) {
|
||||
EmitConstantSwizzleSample(ctx, access);
|
||||
return;
|
||||
}
|
||||
if (access.image.srgbDecode) {
|
||||
ctx.Fail(access.inst, "samples or gathers an sRGB image the device cannot sample, which is not implemented");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue