Merge pull request #1388 from gjonn/feat/agc-color-compare-constant-swizzle

feat(agc): color compare through a view whose X channel is constant
This commit is contained in:
Adrià 2026-10-11 11:21:17 +02:00 committed by GitHub
commit 4c9686fe2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 72 additions and 15 deletions

View File

@ -47,7 +47,8 @@ constexpr std::array<std::uint8_t, 4> Red{0, 64, 128, 255};
alignas(256) std::array<float, Threads * 3> Input{};
alignas(256) std::array<float, Threads * 4> CubeInput{};
alignas(256) std::array<float, Threads> Output{};
alignas(256) std::array<float, Threads * 4> Output{};
std::uint32_t Swizzle = 0xfacu;
alignas(256) std::array<std::uint32_t, Threads * 3> Extra{};
alignas(4096) std::array<std::uint8_t, 4096> Texels{};
@ -71,6 +72,14 @@ alignas(256) constexpr std::array<std::uint32_t, 16> OffsetCode{
0x7e0c0308, 0xf0f80108, 0x00820901, 0xbf8c3f70, 0x34160082, 0xe0701000, 0x8001090b, 0xbf810000,
};
alignas(256) constexpr std::array<std::uint32_t, 17> QuadCode{
0x1614008c, 0xe03c1000, 0x8000010a, 0x7e0802f3, 0x7e0a02f3, 0x7e0c02f3, 0x7e0e02f3, 0xbf8c3f70, 0xf0bc0f08, 0x00820401, 0xbf8c3f70, 0x34160084, 0xe0781000, 0x8001040b, 0xbf810000, 0xbf810000, 0xbf810000,
};
alignas(256) constexpr std::array<std::uint32_t, 17> PairCode{
0x1614008c, 0xe03c1000, 0x8000010a, 0x7e0802f3, 0x7e0a02f3, 0x7e0c02f3, 0x7e0e02f3, 0xbf8c3f70, 0xf0bc0608, 0x00820401, 0xbf8c3f70, 0x34160084, 0xe0781000, 0x8001040b, 0xbf810000, 0xbf810000, 0xbf810000,
};
struct Sampler {
std::uint32_t clamp;
std::uint32_t filter;
@ -91,7 +100,7 @@ std::array<std::uint32_t, 8> TextureDescriptor(std::uint32_t format, bool cube =
static_cast<std::uint32_t>(address >> 8u),
static_cast<std::uint32_t>((address >> 40u) & 0xffu) | (format << 20u) | (((Side - 1u) & 3u) << 30u),
((Side - 1u) >> 2u) | ((Side - 1u) << 14u),
0xfacu | ((cube ? TypeCube : Type2D) << 28u),
Swizzle | ((cube ? TypeCube : Type2D) << 28u),
cube ? 5u : 0u, 0u, 0u, 0u,
};
}
@ -240,6 +249,32 @@ void Run(AgcDriver::VulkanDevice& device, const Sampler& sampler, const char* na
}
}
constexpr std::uint32_t Select(std::uint32_t x, std::uint32_t y, std::uint32_t z, std::uint32_t w) {
return x | (y << 3u) | (z << 6u) | (w << 9u);
}
void RunChannels(AgcDriver::VulkanDevice& device, std::span<const std::uint32_t> code, std::array<std::uint32_t, 4> channels, std::uint32_t registers, std::uint32_t firstChannel, const char* name) {
const Sampler sampler{ClampEdge, FilterBilinear};
Swizzle = Select(channels[0], channels[1], channels[2], channels[3]);
Output.fill(-2.0f);
const auto result = Compile(device, Format8888UNorm, sampler, code);
Swizzle = 0xfacu;
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
device.WaitIdle();
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
const float compared = Expected(tid, sampler, false);
for (std::uint32_t reg = 0; reg < 4u; ++reg) {
float expected = -1.0f;
if (reg < registers) {
const auto select = channels[firstChannel + reg];
expected = select == 0u ? 0.0f : select == 1u ? 1.0f : compared;
}
const float actual = Output[tid * 4u + reg];
Require(std::fabs(actual - expected) <= 1e-4f, std::string(name) + ": thread " + std::to_string(tid) + " register " + std::to_string(reg) + " returned " + std::to_string(actual) + ", expected " + std::to_string(expected));
}
}
}
void RunCube(AgcDriver::VulkanDevice& device, const Sampler& sampler, const char* name) {
FillCubeTexels();
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
@ -313,6 +348,19 @@ int main() {
Run(*device, {ClampWrap, FilterBilinear}, "cached bilinear comparison with offsets", true, true);
}
Reject(*device, Format32Float, {ClampEdge, FilterPoint}, "native comparison with a nonconstant texel offset requires", OffsetCode, false);
RunChannels(*device, QuadCode, {4, 5, 6, 7}, 4, 0, "view R,G,B,A");
RunChannels(*device, QuadCode, {0, 0, 0, 0}, 4, 0, "view 0,0,0,0");
RunChannels(*device, QuadCode, {1, 1, 1, 1}, 4, 0, "view 1,1,1,1");
RunChannels(*device, QuadCode, {0, 4, 1, 5}, 4, 0, "view 0,R,1,G");
RunChannels(*device, QuadCode, {4, 0, 0, 1}, 4, 0, "view R,0,0,1");
RunChannels(*device, QuadCode, {5, 4, 5, 6}, 4, 0, "view G,R,B,A");
RunChannels(*device, QuadCode, {7, 4, 4, 4}, 4, 0, "view A,R,R,R");
RunChannels(*device, PairCode, {0, 4, 4, 4}, 2, 1, "view 0,R,R,R, dmask 6");
Swizzle = Select(4, 2, 4, 4);
Reject(*device, Format8888UNorm, {ClampEdge, FilterPoint}, "every view channel is a constant or a color channel");
Swizzle = Select(5, 5, 5, 5);
Reject(*device, Format8888UNorm, {ClampEdge, FilterPoint}, "a view channel selects red");
Swizzle = 0xfacu;
Reject(*device, Format8888UInt, {ClampEdge, FilterPoint}, "unsupported format");
Reject(*device, Format8888UNorm, {ClampMirror, FilterPoint}, "wrap, clamp-to-edge or clamp-to-border");
Reject(*device, Format8888UNorm, {ClampHalfBorder, FilterPoint}, "wrap, clamp-to-edge or clamp-to-border");

View File

@ -528,7 +528,15 @@ std::uint32_t emulatedCompareState(const ShaderInfo& info, const ResourceSnapsho
const auto reference = colorCompareReference(format);
const auto type = rawImageType(descriptor);
if (type != ImageType::Color2D && type != ImageType::Color2DArray && type != ImageType::Cube) throw std::runtime_error("comparison sampling of a color texture is implemented only for 2D, 2D array and cube views");
if ((descriptorImageSwizzle(descriptor) & 0x7u) != 4u) throw std::runtime_error("comparison sampling of a color texture is implemented only when the view's X channel is red");
bool redChannel = false;
bool compareChannel = false;
for (std::uint32_t channel = 0u; channel < 4u; ++channel) {
const auto select = (descriptorImageSwizzle(descriptor) >> (channel * 3u)) & 0x7u;
if (select == 2u || select == 3u) throw std::runtime_error("comparison sampling of a color texture is implemented only when every view channel is a constant or a color channel");
redChannel = redChannel || select == 4u;
compareChannel = compareChannel || select >= 4u;
}
if (compareChannel && !redChannel) throw std::runtime_error("comparison sampling of a color texture is implemented only when a view channel selects red");
std::optional<std::uint32_t> samplerState;
for (const auto& pair : info.sampledPairs) {
if (pair.image != index) continue;

View File

@ -333,7 +333,7 @@ std::uint32_t ResultVector(SpirvValueEmitContext& ctx, const ImageEmitAccess& ac
std::uint32_t component[4] = {};
for (std::uint32_t index = 0; index < 4u; index++) {
if (dref) {
component[index] = index == 0u ? F32BitsToU32(ctx, value) : ConstantU32(state, 0);
component[index] = F32BitsToU32(ctx, value);
continue;
}
const auto scalar = state.module.AllocateId();
@ -1336,7 +1336,8 @@ void EmitEmulatedCompareSample(SpirvValueEmitContext& ctx, const ImageEmitAccess
else state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 2), coord, ux, uy);
const auto texel = state.module.AllocateId();
state.module.AddFunction(spv::OpImageFetch, ImageVectorType(state, IrTextureNumericClass::Float, 4), texel, descriptor, coord, spv::ImageOperandsLodMask, ConstantU32(state, 0u));
auto red = extract(f32, texel, 0u);
auto red = extract(f32, texel, 3u);
for (std::uint32_t channel = 3u; channel-- > 0u;) red = Select(state, f32, equal(RuntimeImageSwizzle(state, mem.resource, channel), 4u), extract(f32, texel, channel), red);
red = Select(state, f32, equal(modeX, EmulatedCompare::AddressBorder), Select(state, f32, inside(x, width), red, borderRed), red);
red = Select(state, f32, equal(modeY, EmulatedCompare::AddressBorder), Select(state, f32, inside(y, height), red, borderRed), red);
constexpr std::array<spv::Op, 6> operations{spv::OpFOrdLessThan, spv::OpFOrdEqual, spv::OpFOrdLessThanEqual, spv::OpFOrdGreaterThan, spv::OpFOrdNotEqual, spv::OpFOrdGreaterThanEqual};
@ -1377,7 +1378,15 @@ void EmitEmulatedCompareSample(SpirvValueEmitContext& ctx, const ImageEmitAccess
const auto bottom = ext(f32, GLSLstd450FMix, {compareTexel(x0, y1), compareTexel(x1, y1), weightU});
return ext(f32, GLSLstd450FMix, {top, bottom, weightV});
});
ctx.Define(access.inst, TableResult(ctx, access, ResultVector(ctx, access, result, setup.numericClass, true, false)));
const auto channelResult = [&](std::uint32_t channel) {
const auto selector = RuntimeImageSwizzle(state, mem.resource, channel);
return Select(state, f32, equal(selector, 0u), zero, Select(state, f32, equal(selector, 1u), one, result));
};
std::uint32_t channels[4] = {};
for (std::uint32_t channel = 0; channel < 4u; ++channel) channels[channel] = channelResult(channel);
const auto value = state.module.AllocateId();
state.module.AddFunction(spv::OpCompositeConstruct, TypeF32Vector(state, 4), value, channels[0], channels[1], channels[2], channels[3]);
ctx.Define(access.inst, TableResult(ctx, access, ResultVector(ctx, access, value, IrTextureNumericClass::Float, false, false)));
}
void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, const SampleSetup& setup) {

View File

@ -204,15 +204,7 @@ bool TranslationContext::imageSample(const RdnaInstruction& inst) {
IrValue* sampler = getSamplerResource(memory);
IrValue* address = makeImageAddress(inst, inst.source0);
IrValue& result = ir.Emit(IrOpcode::ImageSampleRaw, IrOpcodeType(IrOpcode::ImageSampleRaw), {resource, sampler, address}, addMemoryInfo(memory, inst.programCounter));
const bool dref = (memory.imageSampleFlags & RdnaImageSampleFlagCompare) != 0u;
if (dref && memory.dataBits != 16u) {
IrValue& component = ir.CompositeExtract(result, 0u);
for (std::uint32_t index = 0; index < memory.dataDwords; ++index) {
writeOperand(offsetOperand(inst.destination, index), &component);
}
} else {
writeImageComponents(inst.destination, &result, memory, 4u);
}
writeImageComponents(inst.destination, &result, memory, 4u);
return true;
}