Implement OpImageGather Bug: b/129523279 Tests: dEQP-VK.glsl.texture_gather.* Change-Id: Ie87e600bd787fa832beaf834289f64ef3b590bd8 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31869 Presubmit-Ready: Nicolas Capens <nicolascapens@google.com> Tested-by: Nicolas Capens <nicolascapens@google.com> Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Device/Sampler.hpp b/src/Device/Sampler.hpp index cbe29d8..65313ba 100644 --- a/src/Device/Sampler.hpp +++ b/src/Device/Sampler.hpp
@@ -145,6 +145,7 @@ AddressingMode addressingModeW; MipmapType mipmapFilter; VkComponentMapping swizzle; + int gatherComponent; bool highPrecisionFiltering; bool compareEnable; VkCompareOp compareOp;
diff --git a/src/Pipeline/SamplerCore.cpp b/src/Pipeline/SamplerCore.cpp index fbb74ef..e4ba3e1 100644 --- a/src/Pipeline/SamplerCore.cpp +++ b/src/Pipeline/SamplerCore.cpp
@@ -115,13 +115,13 @@ // TODO: Eliminate int-float-int conversion. lod = Float(As<Int>(Float(lodOrBias.x))); } - else if(function == Base) + else if(function == Base || function == Gather) { lod = Float(0); } else UNREACHABLE("Sampler function %d", int(function)); - if(function != Base && function != Fetch) + if(function != Base && function != Fetch && function != Gather) { lod += *Pointer<Float>(sampler + OFFSET(vk::Sampler, mipLodBias)); @@ -151,7 +151,7 @@ bool seamlessCube = (state.addressingModeU == ADDRESSING_SEAMLESS); bool use32BitFiltering = hasFloatTexture() || hasUnnormalizedIntegerTexture() || force32BitFiltering || seamlessCube || state.unnormalizedCoordinates || state.compareEnable || state.largeTexture || - borderModeActive(); + borderModeActive() || (function == Gather); if(use32BitFiltering) { @@ -242,17 +242,37 @@ } } - if((state.swizzle.r != VK_COMPONENT_SWIZZLE_R) || - (state.swizzle.g != VK_COMPONENT_SWIZZLE_G) || - (state.swizzle.b != VK_COMPONENT_SWIZZLE_B) || - (state.swizzle.a != VK_COMPONENT_SWIZZLE_A)) + if(state.textureFilter != FILTER_GATHER) { - const Vector4f col(c); - auto integer = hasUnnormalizedIntegerTexture(); - applySwizzle(state.swizzle.r, c.x, col, integer); - applySwizzle(state.swizzle.g, c.y, col, integer); - applySwizzle(state.swizzle.b, c.z, col, integer); - applySwizzle(state.swizzle.a, c.w, col, integer); + if((state.swizzle.r != VK_COMPONENT_SWIZZLE_R) || + (state.swizzle.g != VK_COMPONENT_SWIZZLE_G) || + (state.swizzle.b != VK_COMPONENT_SWIZZLE_B) || + (state.swizzle.a != VK_COMPONENT_SWIZZLE_A)) + { + const Vector4f col(c); + bool integer = hasUnnormalizedIntegerTexture(); + applySwizzle(state.swizzle.r, c.x, col, integer); + applySwizzle(state.swizzle.g, c.y, col, integer); + applySwizzle(state.swizzle.b, c.z, col, integer); + applySwizzle(state.swizzle.a, c.w, col, integer); + } + } + else // Gather + { + VkComponentSwizzle swizzle = gatherSwizzle(); + + // R/G/B/A swizzles affect the component collected from each texel earlier. + // Handle the ZERO and ONE cases here because we don't need to know the format. + + if(swizzle == VK_COMPONENT_SWIZZLE_ZERO) + { + c.x = c.y = c.z = c.w = Float4(0); + } + else if(swizzle == VK_COMPONENT_SWIZZLE_ONE) + { + bool integer = hasUnnormalizedIntegerTexture(); + c.x = c.y = c.z = c.w = integer ? As<Float4>(Int4(1)) : RValue<Float4>(Float4(1.0f)); + } } return c; @@ -597,10 +617,20 @@ } else // Gather { - c.x = c10.x; - c.y = c01.x; - c.z = c11.x; - c.w = c00.x; + VkComponentSwizzle swizzle = gatherSwizzle(); + switch(swizzle) + { + case VK_COMPONENT_SWIZZLE_ZERO: + case VK_COMPONENT_SWIZZLE_ONE: + // Handled at the final component swizzle. + break; + default: + c.x = c01[swizzle - VK_COMPONENT_SWIZZLE_R]; + c.y = c11[swizzle - VK_COMPONENT_SWIZZLE_R]; + c.z = c10[swizzle - VK_COMPONENT_SWIZZLE_R]; + c.w = c00[swizzle - VK_COMPONENT_SWIZZLE_R]; + break; + } } } @@ -885,10 +915,20 @@ } else // Gather { - c.x = c10.x; - c.y = c01.x; - c.z = c11.x; - c.w = c00.x; + VkComponentSwizzle swizzle = gatherSwizzle(); + switch(swizzle) + { + case VK_COMPONENT_SWIZZLE_ZERO: + case VK_COMPONENT_SWIZZLE_ONE: + // Handled at the final component swizzle. + break; + default: + c.x = c01[swizzle - VK_COMPONENT_SWIZZLE_R]; + c.y = c11[swizzle - VK_COMPONENT_SWIZZLE_R]; + c.z = c10[swizzle - VK_COMPONENT_SWIZZLE_R]; + c.w = c00[swizzle - VK_COMPONENT_SWIZZLE_R]; + break; + } } } @@ -2024,6 +2064,20 @@ } } + // TODO: Eliminate when the gather + mirror addressing case is handled by mirroring the footprint. + static Int4 mirror(Int4 n) + { + auto positive = CmpNLT(n, Int4(0)); + return (positive & n) | (~positive & (-(Int4(1) + n))); + } + + static Int4 mod(Int4 n, Int4 d) + { + auto x = n % d; + auto positive = CmpNLT(x, Int4(0)); + return (positive & x) | (~positive & (x + d)); + } + void SamplerCore::address(Float4 &uvw, Int4 &xyz0, Int4 &xyz1, Float4 &f, Pointer<Byte> &mipmap, Float4 &texOffset, Int4 &filter, int whd, AddressingMode addressingMode, SamplerFunction function) { if(addressingMode == ADDRESSING_UNUSED) @@ -2071,51 +2125,77 @@ break; } } + else if(state.textureFilter == FILTER_GATHER && addressingMode == ADDRESSING_MIRROR) + { + // Gather requires the 'footprint' of the texels from which a component is taken, to also mirror around. + // Therefore we can't just compute one texel's location and find the other ones at +1 offsets from it. + // Here we handle that case separately by doing the mirroring per texel coordinate. + // TODO: Mirror the footprint by adjusting the sign of the 0.5f and 1 offsets. + + coord = coord * Float4(dim); + coord -= Float4(0.5f); + Float4 floor = Floor(coord); + xyz0 = Int4(floor); + + if(function.option == Offset) + { + xyz0 += As<Int4>(texOffset); + } + + xyz1 = xyz0 + Int4(1); + + xyz0 = (maxXYZ) - mirror(mod(xyz0, Int4(2) * dim) - dim); + xyz1 = (maxXYZ) - mirror(mod(xyz1, Int4(2) * dim) - dim); + + return; + } else { - switch(addressingMode) + if(function.option != Offset) { - case ADDRESSING_CLAMP: - case ADDRESSING_SEAMLESS: - // Linear filtering of cube doesn't require clamping because the coordinates - // are already in [0, 1] range and numerical imprecision is tolerated. - if(addressingMode != ADDRESSING_SEAMLESS || pointFilter) + switch(addressingMode) { - Float4 one = As<Float4>(Int4(oneBits)); - coord = Min(Max(coord, Float4(0.0f)), one); + case ADDRESSING_CLAMP: + case ADDRESSING_SEAMLESS: + // Linear filtering of cube doesn't require clamping because the coordinates + // are already in [0, 1] range and numerical imprecision is tolerated. + if(addressingMode != ADDRESSING_SEAMLESS || pointFilter) + { + Float4 one = As<Float4>(Int4(oneBits)); + coord = Min(Max(coord, Float4(0.0f)), one); + } + break; + case ADDRESSING_MIRROR: + { + Float4 half = As<Float4>(Int4(halfBits)); + Float4 one = As<Float4>(Int4(oneBits)); + Float4 two = As<Float4>(Int4(twoBits)); + coord = one - Abs(two * Frac(coord * half) - one); + } + break; + case ADDRESSING_MIRRORONCE: + { + Float4 half = As<Float4>(Int4(halfBits)); + Float4 one = As<Float4>(Int4(oneBits)); + Float4 two = As<Float4>(Int4(twoBits)); + coord = one - Abs(two * Frac(Min(Max(coord, -one), two) * half) - one); + } + break; + case ADDRESSING_BORDER: + // Don't map to a valid range here. + break; + default: // Wrap + coord = Frac(coord); + break; } - break; - case ADDRESSING_MIRROR: - { - Float4 half = As<Float4>(Int4(halfBits)); - Float4 one = As<Float4>(Int4(oneBits)); - Float4 two = As<Float4>(Int4(twoBits)); - coord = one - Abs(two * Frac(coord * half) - one); - } - break; - case ADDRESSING_MIRRORONCE: - { - Float4 half = As<Float4>(Int4(halfBits)); - Float4 one = As<Float4>(Int4(oneBits)); - Float4 two = As<Float4>(Int4(twoBits)); - coord = one - Abs(two * Frac(Min(Max(coord, -one), two) * half) - one); - } - break; - case ADDRESSING_BORDER: - // Don't map to a valid range here. - break; - default: // Wrap - coord = Frac(coord); - break; } coord = coord * Float4(dim); } - if(state.textureFilter == FILTER_POINT || - state.textureFilter == FILTER_GATHER) + if(state.textureFilter == FILTER_POINT) { - if(addressingMode == ADDRESSING_BORDER) + if(addressingMode == ADDRESSING_BORDER || function.option == Offset) { xyz0 = Int4(Floor(coord)); } @@ -2176,8 +2256,8 @@ xyz1 = Min(Max(xyz1, Int4(0)), maxXYZ); break; default: // Wrap - xyz0 = (xyz0 + dim * Int4(-MIN_PROGRAM_TEXEL_OFFSET)) % dim; - xyz1 = (xyz1 + dim * Int4(-MIN_PROGRAM_TEXEL_OFFSET)) % dim; + xyz0 = mod(xyz0, dim); + xyz1 = mod(xyz1, dim); break; } } @@ -2292,4 +2372,18 @@ state.addressingModeV == ADDRESSING_BORDER || state.addressingModeW == ADDRESSING_BORDER; } + + VkComponentSwizzle SamplerCore::gatherSwizzle() const + { + switch(state.gatherComponent) + { + case 0: return state.swizzle.r; + case 1: return state.swizzle.g; + case 2: return state.swizzle.b; + case 3: return state.swizzle.a; + default: + UNREACHABLE("Invalid component"); + return VK_COMPONENT_SWIZZLE_R; + } + } }
diff --git a/src/Pipeline/SamplerCore.hpp b/src/Pipeline/SamplerCore.hpp index bcca3a0..068139b 100644 --- a/src/Pipeline/SamplerCore.hpp +++ b/src/Pipeline/SamplerCore.hpp
@@ -36,7 +36,8 @@ Fetch, // Use provided integer coordinates. Base, // Sample base level. Query, // Return implicit LOD. - SAMPLER_METHOD_LAST = Query, + Gather, // Return one channel of each texel in footprint. + SAMPLER_METHOD_LAST = Gather, }; enum SamplerOption @@ -109,6 +110,7 @@ bool hasYuvFormat() const; bool isRGBComponent(int component) const; bool borderModeActive() const; + VkComponentSwizzle gatherSwizzle() const; Pointer<Byte> &constants; const Sampler &state;
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp index 755487e..e2362b2 100644 --- a/src/Pipeline/SpirvShader.cpp +++ b/src/Pipeline/SpirvShader.cpp
@@ -935,6 +935,8 @@ case spv::OpImageSampleProjExplicitLod: case spv::OpImageSampleProjDrefImplicitLod: case spv::OpImageSampleProjDrefExplicitLod: + case spv::OpImageGather: + case spv::OpImageDrefGather: case spv::OpImageFetch: case spv::OpImageQuerySizeLod: case spv::OpImageQuerySize: @@ -2489,6 +2491,12 @@ case spv::OpImageSampleProjDrefExplicitLod: return EmitImageSampleExplicitLod(ProjDref, insn, state); + case spv::OpImageGather: + return EmitImageGather(None, insn, state); + + case spv::OpImageDrefGather: + return EmitImageGather(Dref, insn, state); + case spv::OpImageFetch: return EmitImageFetch(insn, state); @@ -4659,6 +4667,14 @@ return EmitImageSample({variant, Implicit}, insn, state); } + SpirvShader::EmitResult SpirvShader::EmitImageGather(Variant variant, InsnIterator insn, EmitState *state) const + { + ImageInstruction instruction = {variant, Gather}; + instruction.gatherComponent = !instruction.isDref() ? getObject(insn.word(5)).constantValue[0] : 0; + + return EmitImageSample(instruction, insn, state); + } + SpirvShader::EmitResult SpirvShader::EmitImageSampleExplicitLod(Variant variant, InsnIterator insn, EmitState *state) const { auto isDref = (variant == Dref) || (variant == ProjDref); @@ -4714,7 +4730,7 @@ Object::ID offsetId = 0; bool sample = false; - uint32_t operand = instruction.isDref() ? 6 : 5; + uint32_t operand = (instruction.isDref() || instruction.samplerMethod == Gather) ? 6 : 5; if(insn.wordCount() > operand) {
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp index c8b9f93..cad734d 100644 --- a/src/Pipeline/SpirvShader.hpp +++ b/src/Pipeline/SpirvShader.hpp
@@ -524,6 +524,7 @@ uint32_t variant : BITS(VARIANT_LAST); uint32_t samplerMethod : BITS(SAMPLER_METHOD_LAST); uint32_t samplerOption : BITS(SAMPLER_OPTION_LAST); + uint32_t gatherComponent : 2; // Parameters are passed to the sampling routine in this order: uint32_t coordinates : 3; // 1-4 (does not contain projection component) @@ -937,6 +938,7 @@ EmitResult EmitPhi(InsnIterator insn, EmitState *state) const; EmitResult EmitImageSampleImplicitLod(Variant variant, InsnIterator insn, EmitState *state) const; EmitResult EmitImageSampleExplicitLod(Variant variant, InsnIterator insn, EmitState *state) const; + EmitResult EmitImageGather(Variant variant, InsnIterator insn, EmitState *state) const; EmitResult EmitImageFetch(InsnIterator insn, EmitState *state) const; EmitResult EmitImageSample(ImageInstruction instruction, InsnIterator insn, EmitState *state) const; EmitResult EmitImageQuerySizeLod(InsnIterator insn, EmitState *state) const;
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp index 9d3dc8a..0d0e373 100644 --- a/src/Pipeline/SpirvShaderSampling.cpp +++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -78,7 +78,7 @@ Sampler samplerState = {}; samplerState.textureType = convertTextureType(type); samplerState.textureFormat = imageDescriptor->format; - samplerState.textureFilter = convertFilterMode(sampler); + samplerState.textureFilter = (instruction.samplerMethod == Gather) ? FILTER_GATHER : convertFilterMode(sampler); samplerState.border = sampler->borderColor; samplerState.addressingModeU = convertAddressingMode(0, sampler->addressModeU, type); @@ -87,6 +87,7 @@ samplerState.mipmapFilter = convertMipmapMode(sampler); samplerState.swizzle = imageDescriptor->swizzle; + samplerState.gatherComponent = instruction.gatherComponent; samplerState.highPrecisionFiltering = false; samplerState.compareEnable = (sampler->compareEnable == VK_TRUE); samplerState.compareOp = sampler->compareOp;