| /*------------------------------------------------------------------------- |
| * drawElements Quality Program Tester Core |
| * ---------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Texture compare (shadow) result verifier. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "tcuTexCompareVerifier.hpp" |
| #include "tcuTexVerifierUtil.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuVectorUtil.hpp" |
| #include "deMath.h" |
| |
| namespace tcu |
| { |
| |
| using namespace TexVerifierUtil; |
| |
| // Generic utilities |
| |
| #if defined(DE_DEBUG) |
| static bool isSamplerSupported (const Sampler& sampler) |
| { |
| return sampler.compare != Sampler::COMPAREMODE_NONE && |
| isWrapModeSupported(sampler.wrapS) && |
| isWrapModeSupported(sampler.wrapT) && |
| isWrapModeSupported(sampler.wrapR); |
| } |
| #endif // DE_DEBUG |
| |
| struct CmpResultSet |
| { |
| bool isTrue; |
| bool isFalse; |
| |
| CmpResultSet (void) |
| : isTrue (false) |
| , isFalse (false) |
| { |
| } |
| }; |
| |
| static CmpResultSet execCompare (const Sampler::CompareMode compareMode, |
| const float cmpValue_, |
| const float cmpReference_, |
| const int referenceBits, |
| const bool isFixedPoint) |
| { |
| const bool clampValues = isFixedPoint; // if comparing against a floating point texture, ref (and value) is not clamped |
| const float cmpValue = (clampValues) ? (de::clamp(cmpValue_, 0.0f, 1.0f)) : (cmpValue_); |
| const float cmpReference = (clampValues) ? (de::clamp(cmpReference_, 0.0f, 1.0f)) : (cmpReference_); |
| const float err = computeFixedPointError(referenceBits); |
| CmpResultSet res; |
| |
| switch (compareMode) |
| { |
| case Sampler::COMPAREMODE_LESS: |
| res.isTrue = cmpReference-err < cmpValue; |
| res.isFalse = cmpReference+err >= cmpValue; |
| break; |
| |
| case Sampler::COMPAREMODE_LESS_OR_EQUAL: |
| res.isTrue = cmpReference-err <= cmpValue; |
| res.isFalse = cmpReference+err > cmpValue; |
| break; |
| |
| case Sampler::COMPAREMODE_GREATER: |
| res.isTrue = cmpReference+err > cmpValue; |
| res.isFalse = cmpReference-err <= cmpValue; |
| break; |
| |
| case Sampler::COMPAREMODE_GREATER_OR_EQUAL: |
| res.isTrue = cmpReference+err >= cmpValue; |
| res.isFalse = cmpReference-err < cmpValue; |
| break; |
| |
| case Sampler::COMPAREMODE_EQUAL: |
| res.isTrue = de::inRange(cmpValue, cmpReference-err, cmpReference+err); |
| res.isFalse = err != 0.0f || cmpValue != cmpReference; |
| break; |
| |
| case Sampler::COMPAREMODE_NOT_EQUAL: |
| res.isTrue = err != 0.0f || cmpValue != cmpReference; |
| res.isFalse = de::inRange(cmpValue, cmpReference-err, cmpReference+err); |
| break; |
| |
| case Sampler::COMPAREMODE_ALWAYS: |
| res.isTrue = true; |
| break; |
| |
| case Sampler::COMPAREMODE_NEVER: |
| res.isFalse = true; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| DE_ASSERT(res.isTrue || res.isFalse); |
| return res; |
| } |
| |
| static inline bool isResultInSet (const CmpResultSet resultSet, const float result, const int resultBits) |
| { |
| const float err = computeFixedPointError(resultBits); |
| const float minR = result-err; |
| const float maxR = result+err; |
| |
| return (resultSet.isTrue && de::inRange(1.0f, minR, maxR)) || |
| (resultSet.isFalse && de::inRange(0.0f, minR, maxR)); |
| } |
| |
| static inline bool coordsInBounds (const ConstPixelBufferAccess& access, int x, int y, int z) |
| { |
| return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) && de::inBounds(z, 0, access.getDepth()); |
| } |
| |
| static float lookupDepth (const tcu::ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k) |
| { |
| if (coordsInBounds(access, i, j, k)) |
| return access.getPixDepth(i, j, k); |
| else |
| return sampleTextureBorder<float>(access.getFormat(), sampler).x(); |
| } |
| |
| // lookup depth value at a point that is guaranteed to not sample border such as cube map faces. |
| static float lookupDepthNoBorder (const tcu::ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k = 0) |
| { |
| DE_UNREF(sampler); |
| DE_ASSERT(coordsInBounds(access, i, j, k)); |
| return access.getPixDepth(i, j, k); |
| } |
| |
| // Values are in order (0,0), (1,0), (0,1), (1,1) |
| static float bilinearInterpolate (const Vec4& values, const float x, const float y) |
| { |
| const float v00 = values[0]; |
| const float v10 = values[1]; |
| const float v01 = values[2]; |
| const float v11 = values[3]; |
| const float res = v00*(1.0f-x)*(1.0f-y) + v10*x*(1.0f-y) + v01*(1.0f-x)*y + v11*x*y; |
| return res; |
| } |
| |
| static bool isFixedPointDepthTextureFormat (const tcu::TextureFormat& format) |
| { |
| const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(format.type); |
| |
| if (format.order == TextureFormat::D) |
| { |
| // depth internal formats cannot be non-normalized integers |
| return channelClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT; |
| } |
| else if (format.order == TextureFormat::DS) |
| { |
| // combined formats have no single channel class, detect format manually |
| switch (format.type) |
| { |
| case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: return false; |
| case tcu::TextureFormat::UNSIGNED_INT_16_8_8: return true; |
| case tcu::TextureFormat::UNSIGNED_INT_24_8: return true; |
| case tcu::TextureFormat::UNSIGNED_INT_24_8_REV: return true; |
| |
| default: |
| { |
| // unknown format |
| DE_ASSERT(false); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isLinearCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec2& depths, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f); |
| |
| const float d0 = depths[0]; |
| const float d1 = depths[1]; |
| |
| const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const deUint32 isTrue = (deUint32(cmp0.isTrue)<<0) |
| | (deUint32(cmp1.isTrue)<<1); |
| const deUint32 isFalse = (deUint32(cmp0.isFalse)<<0) |
| | (deUint32(cmp1.isFalse)<<1); |
| |
| // Interpolation parameters |
| const float f0 = fBounds.x(); |
| const float f1 = fBounds.y(); |
| |
| // Error parameters |
| const float pcfErr = computeFixedPointError(prec.pcfBits); |
| const float resErr = computeFixedPointError(prec.resultBits); |
| const float totalErr = pcfErr+resErr; |
| |
| // Iterate over all valid combinations. |
| for (deUint32 comb = 0; comb < (1<<2); comb++) |
| { |
| // Filter out invalid combinations. |
| if (((comb & isTrue) | (~comb & isFalse)) != (1<<2)-1) |
| continue; |
| |
| const bool cmp0True = ((comb>>0)&1) != 0; |
| const bool cmp1True = ((comb>>1)&1) != 0; |
| |
| const float ref0 = cmp0True ? 1.0f : 0.0f; |
| const float ref1 = cmp1True ? 1.0f : 0.0f; |
| |
| const float v0 = ref0*(1.0f-f0) + ref1*f0; |
| const float v1 = ref0*(1.0f-f1) + ref1*f1; |
| const float minV = de::min(v0, v1); |
| const float maxV = de::max(v0, v1); |
| const float minR = minV-totalErr; |
| const float maxR = maxV+totalErr; |
| |
| if (de::inRange(result, minR, maxR)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static inline BVec4 extractBVec4 (const deUint32 val, int offset) |
| { |
| return BVec4(((val>>(offset+0))&1) != 0, |
| ((val>>(offset+1))&1) != 0, |
| ((val>>(offset+2))&1) != 0, |
| ((val>>(offset+3))&1) != 0); |
| } |
| |
| static bool isBilinearAnyCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec4& depths, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| DE_ASSERT(prec.pcfBits == 0); |
| |
| const float d0 = depths[0]; |
| const float d1 = depths[1]; |
| const float d2 = depths[2]; |
| const float d3 = depths[3]; |
| |
| const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp2 = execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp3 = execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const bool canBeTrue = cmp0.isTrue || cmp1.isTrue || cmp2.isTrue || cmp3.isTrue; |
| const bool canBeFalse = cmp0.isFalse || cmp1.isFalse || cmp2.isFalse || cmp3.isFalse; |
| |
| const float resErr = computeFixedPointError(prec.resultBits); |
| |
| const float minBound = canBeFalse ? 0.0f : 1.0f; |
| const float maxBound = canBeTrue ? 1.0f : 0.0f; |
| |
| return de::inRange(result, minBound-resErr, maxBound+resErr); |
| } |
| |
| static bool isBilinearPCFCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec4& depths, |
| const Vec2& xBounds, |
| const Vec2& yBounds, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| DE_ASSERT(0.0f <= xBounds.x() && xBounds.x() <= xBounds.y() && xBounds.y() <= 1.0f); |
| DE_ASSERT(0.0f <= yBounds.x() && yBounds.x() <= yBounds.y() && yBounds.y() <= 1.0f); |
| DE_ASSERT(prec.pcfBits > 0); |
| |
| const float d0 = depths[0]; |
| const float d1 = depths[1]; |
| const float d2 = depths[2]; |
| const float d3 = depths[3]; |
| |
| const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp2 = execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp3 = execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const deUint32 isTrue = (deUint32(cmp0.isTrue)<<0) |
| | (deUint32(cmp1.isTrue)<<1) |
| | (deUint32(cmp2.isTrue)<<2) |
| | (deUint32(cmp3.isTrue)<<3); |
| const deUint32 isFalse = (deUint32(cmp0.isFalse)<<0) |
| | (deUint32(cmp1.isFalse)<<1) |
| | (deUint32(cmp2.isFalse)<<2) |
| | (deUint32(cmp3.isFalse)<<3); |
| |
| // Interpolation parameters |
| const float x0 = xBounds.x(); |
| const float x1 = xBounds.y(); |
| const float y0 = yBounds.x(); |
| const float y1 = yBounds.y(); |
| |
| // Error parameters |
| const float pcfErr = computeFixedPointError(prec.pcfBits); |
| const float resErr = computeFixedPointError(prec.resultBits); |
| const float totalErr = pcfErr+resErr; |
| |
| // Iterate over all valid combinations. |
| // \note It is not enough to compute minmax over all possible result sets, as ranges may |
| // not necessarily overlap, i.e. there are gaps between valid ranges. |
| for (deUint32 comb = 0; comb < (1<<4); comb++) |
| { |
| // Filter out invalid combinations: |
| // 1) True bit is set in comb but not in isTrue => sample can not be true |
| // 2) True bit is NOT set in comb and not in isFalse => sample can not be false |
| if (((comb & isTrue) | (~comb & isFalse)) != (1<<4)-1) |
| continue; |
| |
| const BVec4 cmpTrue = extractBVec4(comb, 0); |
| const Vec4 refVal = select(Vec4(1.0f), Vec4(0.0f), cmpTrue); |
| |
| const float v0 = bilinearInterpolate(refVal, x0, y0); |
| const float v1 = bilinearInterpolate(refVal, x1, y0); |
| const float v2 = bilinearInterpolate(refVal, x0, y1); |
| const float v3 = bilinearInterpolate(refVal, x1, y1); |
| const float minV = de::min(v0, de::min(v1, de::min(v2, v3))); |
| const float maxV = de::max(v0, de::max(v1, de::max(v2, v3))); |
| const float minR = minV-totalErr; |
| const float maxR = maxV+totalErr; |
| |
| if (de::inRange(result, minR, maxR)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool isBilinearCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec4& depths, |
| const Vec2& xBounds, |
| const Vec2& yBounds, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| if (prec.pcfBits > 0) |
| return isBilinearPCFCompareValid(compareMode, prec, depths, xBounds, yBounds, cmpReference, result, isFixedPointDepth); |
| else |
| return isBilinearAnyCompareValid(compareMode, prec, depths, cmpReference, result, isFixedPointDepth); |
| } |
| |
| static bool isTrilinearAnyCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec4& depths0, |
| const Vec4& depths1, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| DE_ASSERT(prec.pcfBits == 0); |
| |
| const CmpResultSet cmp00 = execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp01 = execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp02 = execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp03 = execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const CmpResultSet cmp10 = execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp11 = execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp12 = execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp13 = execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const bool canBeTrue = cmp00.isTrue || |
| cmp01.isTrue || |
| cmp02.isTrue || |
| cmp03.isTrue || |
| cmp10.isTrue || |
| cmp11.isTrue || |
| cmp12.isTrue || |
| cmp13.isTrue; |
| const bool canBeFalse = cmp00.isFalse || |
| cmp01.isFalse || |
| cmp02.isFalse || |
| cmp03.isFalse || |
| cmp10.isFalse || |
| cmp11.isFalse || |
| cmp12.isFalse || |
| cmp13.isFalse; |
| |
| const float resErr = computeFixedPointError(prec.resultBits); |
| |
| const float minBound = canBeFalse ? 0.0f : 1.0f; |
| const float maxBound = canBeTrue ? 1.0f : 0.0f; |
| |
| return de::inRange(result, minBound-resErr, maxBound+resErr); |
| } |
| |
| static bool isTrilinearPCFCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec4& depths0, |
| const Vec4& depths1, |
| const Vec2& xBounds0, |
| const Vec2& yBounds0, |
| const Vec2& xBounds1, |
| const Vec2& yBounds1, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| DE_ASSERT(0.0f <= xBounds0.x() && xBounds0.x() <= xBounds0.y() && xBounds0.y() <= 1.0f); |
| DE_ASSERT(0.0f <= yBounds0.x() && yBounds0.x() <= yBounds0.y() && yBounds0.y() <= 1.0f); |
| DE_ASSERT(0.0f <= xBounds1.x() && xBounds1.x() <= xBounds1.y() && xBounds1.y() <= 1.0f); |
| DE_ASSERT(0.0f <= yBounds1.x() && yBounds1.x() <= yBounds1.y() && yBounds1.y() <= 1.0f); |
| DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f); |
| DE_ASSERT(prec.pcfBits > 0); |
| |
| const CmpResultSet cmp00 = execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp01 = execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp02 = execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp03 = execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const CmpResultSet cmp10 = execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp11 = execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp12 = execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth); |
| const CmpResultSet cmp13 = execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| const deUint32 isTrue = (deUint32(cmp00.isTrue)<<0) |
| | (deUint32(cmp01.isTrue)<<1) |
| | (deUint32(cmp02.isTrue)<<2) |
| | (deUint32(cmp03.isTrue)<<3) |
| | (deUint32(cmp10.isTrue)<<4) |
| | (deUint32(cmp11.isTrue)<<5) |
| | (deUint32(cmp12.isTrue)<<6) |
| | (deUint32(cmp13.isTrue)<<7); |
| const deUint32 isFalse = (deUint32(cmp00.isFalse)<<0) |
| | (deUint32(cmp01.isFalse)<<1) |
| | (deUint32(cmp02.isFalse)<<2) |
| | (deUint32(cmp03.isFalse)<<3) |
| | (deUint32(cmp10.isFalse)<<4) |
| | (deUint32(cmp11.isFalse)<<5) |
| | (deUint32(cmp12.isFalse)<<6) |
| | (deUint32(cmp13.isFalse)<<7); |
| |
| // Error parameters |
| const float pcfErr = computeFixedPointError(prec.pcfBits); |
| const float resErr = computeFixedPointError(prec.resultBits); |
| const float totalErr = pcfErr+resErr; |
| |
| // Iterate over all valid combinations. |
| for (deUint32 comb = 0; comb < (1<<8); comb++) |
| { |
| // Filter out invalid combinations. |
| if (((comb & isTrue) | (~comb & isFalse)) != (1<<8)-1) |
| continue; |
| |
| const BVec4 cmpTrue0 = extractBVec4(comb, 0); |
| const BVec4 cmpTrue1 = extractBVec4(comb, 4); |
| const Vec4 refVal0 = select(Vec4(1.0f), Vec4(0.0f), cmpTrue0); |
| const Vec4 refVal1 = select(Vec4(1.0f), Vec4(0.0f), cmpTrue1); |
| |
| // Bilinear interpolation within levels. |
| const float v00 = bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.x()); |
| const float v01 = bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.x()); |
| const float v02 = bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.y()); |
| const float v03 = bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.y()); |
| const float minV0 = de::min(v00, de::min(v01, de::min(v02, v03))); |
| const float maxV0 = de::max(v00, de::max(v01, de::max(v02, v03))); |
| |
| const float v10 = bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.x()); |
| const float v11 = bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.x()); |
| const float v12 = bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.y()); |
| const float v13 = bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.y()); |
| const float minV1 = de::min(v10, de::min(v11, de::min(v12, v13))); |
| const float maxV1 = de::max(v10, de::max(v11, de::max(v12, v13))); |
| |
| // Compute min-max bounds by filtering between minimum bounds and maximum bounds between levels. |
| // HW can end up choosing pretty much any of samples between levels, and thus interpolating |
| // between minimums should yield lower bound for range, and same for upper bound. |
| // \todo [2013-07-17 pyry] This seems separable? Can this be optimized? At least ranges could be pre-computed and later combined. |
| const float minF0 = minV0*(1.0f-fBounds.x()) + minV1*fBounds.x(); |
| const float minF1 = minV0*(1.0f-fBounds.y()) + minV1*fBounds.y(); |
| const float maxF0 = maxV0*(1.0f-fBounds.x()) + maxV1*fBounds.x(); |
| const float maxF1 = maxV0*(1.0f-fBounds.y()) + maxV1*fBounds.y(); |
| |
| const float minF = de::min(minF0, minF1); |
| const float maxF = de::max(maxF0, maxF1); |
| |
| const float minR = minF-totalErr; |
| const float maxR = maxF+totalErr; |
| |
| if (de::inRange(result, minR, maxR)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool isTrilinearCompareValid (const Sampler::CompareMode compareMode, |
| const TexComparePrecision& prec, |
| const Vec4& depths0, |
| const Vec4& depths1, |
| const Vec2& xBounds0, |
| const Vec2& yBounds0, |
| const Vec2& xBounds1, |
| const Vec2& yBounds1, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result, |
| const bool isFixedPointDepth) |
| { |
| if (prec.pcfBits > 0) |
| return isTrilinearPCFCompareValid(compareMode, prec, depths0, depths1, xBounds0, yBounds0, xBounds1, yBounds1, fBounds, cmpReference, result, isFixedPointDepth); |
| else |
| return isTrilinearAnyCompareValid(compareMode, prec, depths0, depths1, cmpReference, result, isFixedPointDepth); |
| } |
| |
| static bool isNearestCompareResultValid (const ConstPixelBufferAccess& level, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const int coordZ, |
| const float cmpReference, |
| const float result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat()); |
| const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinates - without wrap mode |
| const int minI = deFloorFloatToInt32(uBounds.x()); |
| const int maxI = deFloorFloatToInt32(uBounds.y()); |
| const int minJ = deFloorFloatToInt32(vBounds.x()); |
| const int maxJ = deFloorFloatToInt32(vBounds.y()); |
| |
| for (int j = minJ; j <= maxJ; j++) |
| { |
| for (int i = minI; i <= maxI; i++) |
| { |
| const int x = wrap(sampler.wrapS, i, level.getWidth()); |
| const int y = wrap(sampler.wrapT, j, level.getHeight()); |
| const float depth = lookupDepth(level, sampler, x, y, coordZ); |
| const CmpResultSet resSet = execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| if (isResultInSet(resSet, result, prec.resultBits)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isLinearCompareResultValid (const ConstPixelBufferAccess& level, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const int coordZ, |
| const float cmpReference, |
| const float result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat()); |
| const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinate bounds for (x0,y0) - without wrap mode |
| const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); |
| const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); |
| const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); |
| const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); |
| |
| const int w = level.getWidth(); |
| const int h = level.getHeight(); |
| |
| // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. |
| |
| for (int j = minJ; j <= maxJ; j++) |
| { |
| for (int i = minI; i <= maxI; i++) |
| { |
| // Wrapped coordinates |
| const int x0 = wrap(sampler.wrapS, i , w); |
| const int x1 = wrap(sampler.wrapS, i+1, w); |
| const int y0 = wrap(sampler.wrapT, j , h); |
| const int y1 = wrap(sampler.wrapT, j+1, h); |
| |
| // Bounds for filtering factors |
| const float minA = de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f); |
| const float maxA = de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f); |
| const float minB = de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f); |
| const float maxB = de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f); |
| |
| const Vec4 depths (lookupDepth(level, sampler, x0, y0, coordZ), |
| lookupDepth(level, sampler, x1, y0, coordZ), |
| lookupDepth(level, sampler, x0, y1, coordZ), |
| lookupDepth(level, sampler, x1, y1, coordZ)); |
| |
| if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isLevelCompareResultValid (const ConstPixelBufferAccess& level, |
| const Sampler& sampler, |
| const Sampler::FilterMode filterMode, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const int coordZ, |
| const float cmpReference, |
| const float result) |
| { |
| if (filterMode == Sampler::LINEAR) |
| return isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result); |
| else |
| return isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result); |
| } |
| |
| static bool isNearestMipmapLinearCompareResultValid (const ConstPixelBufferAccess& level0, |
| const ConstPixelBufferAccess& level1, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const int coordZ, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat()); |
| |
| const int w0 = level0.getWidth(); |
| const int w1 = level1.getWidth(); |
| const int h0 = level0.getHeight(); |
| const int h1 = level1.getHeight(); |
| |
| const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinates - without wrap mode |
| const int minI0 = deFloorFloatToInt32(uBounds0.x()); |
| const int maxI0 = deFloorFloatToInt32(uBounds0.y()); |
| const int minI1 = deFloorFloatToInt32(uBounds1.x()); |
| const int maxI1 = deFloorFloatToInt32(uBounds1.y()); |
| const int minJ0 = deFloorFloatToInt32(vBounds0.x()); |
| const int maxJ0 = deFloorFloatToInt32(vBounds0.y()); |
| const int minJ1 = deFloorFloatToInt32(vBounds1.x()); |
| const int maxJ1 = deFloorFloatToInt32(vBounds1.y()); |
| |
| for (int j0 = minJ0; j0 <= maxJ0; j0++) |
| { |
| for (int i0 = minI0; i0 <= maxI0; i0++) |
| { |
| const float depth0 = lookupDepth(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ); |
| |
| for (int j1 = minJ1; j1 <= maxJ1; j1++) |
| { |
| for (int i1 = minI1; i1 <= maxI1; i1++) |
| { |
| const float depth1 = lookupDepth(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ); |
| |
| if (isLinearCompareValid(sampler.compare, prec, Vec2(depth0, depth1), fBounds, cmpReference, result, isFixedPointDepth)) |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isLinearMipmapLinearCompareResultValid (const ConstPixelBufferAccess& level0, |
| const ConstPixelBufferAccess& level1, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const int coordZ, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat()); |
| |
| // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent. |
| // Right now this allows pairing any two valid bilinear quads. |
| |
| const int w0 = level0.getWidth(); |
| const int w1 = level1.getWidth(); |
| const int h0 = level0.getHeight(); |
| const int h1 = level1.getHeight(); |
| |
| const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinates - without wrap mode |
| const int minI0 = deFloorFloatToInt32(uBounds0.x()-0.5f); |
| const int maxI0 = deFloorFloatToInt32(uBounds0.y()-0.5f); |
| const int minI1 = deFloorFloatToInt32(uBounds1.x()-0.5f); |
| const int maxI1 = deFloorFloatToInt32(uBounds1.y()-0.5f); |
| const int minJ0 = deFloorFloatToInt32(vBounds0.x()-0.5f); |
| const int maxJ0 = deFloorFloatToInt32(vBounds0.y()-0.5f); |
| const int minJ1 = deFloorFloatToInt32(vBounds1.x()-0.5f); |
| const int maxJ1 = deFloorFloatToInt32(vBounds1.y()-0.5f); |
| |
| for (int j0 = minJ0; j0 <= maxJ0; j0++) |
| { |
| for (int i0 = minI0; i0 <= maxI0; i0++) |
| { |
| const float minA0 = de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f); |
| const float maxA0 = de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f); |
| const float minB0 = de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f); |
| const float maxB0 = de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f); |
| Vec4 depths0; |
| |
| { |
| const int x0 = wrap(sampler.wrapS, i0 , w0); |
| const int x1 = wrap(sampler.wrapS, i0+1, w0); |
| const int y0 = wrap(sampler.wrapT, j0 , h0); |
| const int y1 = wrap(sampler.wrapT, j0+1, h0); |
| |
| depths0[0] = lookupDepth(level0, sampler, x0, y0, coordZ); |
| depths0[1] = lookupDepth(level0, sampler, x1, y0, coordZ); |
| depths0[2] = lookupDepth(level0, sampler, x0, y1, coordZ); |
| depths0[3] = lookupDepth(level0, sampler, x1, y1, coordZ); |
| } |
| |
| for (int j1 = minJ1; j1 <= maxJ1; j1++) |
| { |
| for (int i1 = minI1; i1 <= maxI1; i1++) |
| { |
| const float minA1 = de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f); |
| const float maxA1 = de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f); |
| const float minB1 = de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f); |
| const float maxB1 = de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f); |
| Vec4 depths1; |
| |
| { |
| const int x0 = wrap(sampler.wrapS, i1 , w1); |
| const int x1 = wrap(sampler.wrapS, i1+1, w1); |
| const int y0 = wrap(sampler.wrapT, j1 , h1); |
| const int y1 = wrap(sampler.wrapT, j1+1, h1); |
| |
| depths1[0] = lookupDepth(level1, sampler, x0, y0, coordZ); |
| depths1[1] = lookupDepth(level1, sampler, x1, y0, coordZ); |
| depths1[2] = lookupDepth(level1, sampler, x0, y1, coordZ); |
| depths1[3] = lookupDepth(level1, sampler, x1, y1, coordZ); |
| } |
| |
| if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, |
| Vec2(minA0, maxA0), Vec2(minB0, maxB0), |
| Vec2(minA1, maxA1), Vec2(minB1, maxB1), |
| fBounds, cmpReference, result, isFixedPointDepth)) |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isMipmapLinearCompareResultValid (const ConstPixelBufferAccess& level0, |
| const ConstPixelBufferAccess& level1, |
| const Sampler& sampler, |
| const Sampler::FilterMode levelFilter, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const int coordZ, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result) |
| { |
| if (levelFilter == Sampler::LINEAR) |
| return isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result); |
| else |
| return isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result); |
| } |
| |
| bool isTexCompareResultValid (const Texture2DView& texture, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const Vec2& lodBounds, |
| const float cmpReference, |
| const float result) |
| { |
| const float minLod = lodBounds.x(); |
| const float maxLod = lodBounds.y(); |
| const bool canBeMagnified = minLod <= sampler.lodThreshold; |
| const bool canBeMinified = maxLod > sampler.lodThreshold; |
| |
| DE_ASSERT(isSamplerSupported(sampler)); |
| |
| if (canBeMagnified) |
| { |
| if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference, result)) |
| return true; |
| } |
| |
| if (canBeMinified) |
| { |
| const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter); |
| const bool isLinearMipmap = isLinearMipmapFilter(sampler.minFilter); |
| const int minTexLevel = 0; |
| const int maxTexLevel = texture.getNumLevels()-1; |
| |
| DE_ASSERT(minTexLevel < maxTexLevel); |
| |
| if (isLinearMipmap) |
| { |
| const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1); |
| const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1); |
| |
| DE_ASSERT(minLevel <= maxLevel); |
| |
| for (int level = minLevel; level <= maxLevel; level++) |
| { |
| const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f); |
| const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f); |
| |
| if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), cmpReference, result)) |
| return true; |
| } |
| } |
| else if (isNearestMipmap) |
| { |
| // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made |
| // decision to allow floor(lod + 0.5) as well. |
| const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel); |
| const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel); |
| |
| DE_ASSERT(minLevel <= maxLevel); |
| |
| for (int level = minLevel; level <= maxLevel; level++) |
| { |
| if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, cmpReference, result)) |
| return true; |
| } |
| } |
| else |
| { |
| if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference, result)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isSeamplessLinearMipmapLinearCompareResultValid (const TextureCubeView& texture, |
| const int baseLevelNdx, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const CubeFaceFloatCoords& coords, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(baseLevelNdx, CUBEFACE_NEGATIVE_X).getFormat()); |
| const int size0 = texture.getLevelFace(baseLevelNdx, coords.face).getWidth(); |
| const int size1 = texture.getLevelFace(baseLevelNdx+1, coords.face).getWidth(); |
| |
| const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.s, prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.s, prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.t, prec.coordBits.y(), prec.uvwBits.y()); |
| const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.t, prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinates - without wrap mode |
| const int minI0 = deFloorFloatToInt32(uBounds0.x()-0.5f); |
| const int maxI0 = deFloorFloatToInt32(uBounds0.y()-0.5f); |
| const int minI1 = deFloorFloatToInt32(uBounds1.x()-0.5f); |
| const int maxI1 = deFloorFloatToInt32(uBounds1.y()-0.5f); |
| const int minJ0 = deFloorFloatToInt32(vBounds0.x()-0.5f); |
| const int maxJ0 = deFloorFloatToInt32(vBounds0.y()-0.5f); |
| const int minJ1 = deFloorFloatToInt32(vBounds1.x()-0.5f); |
| const int maxJ1 = deFloorFloatToInt32(vBounds1.y()-0.5f); |
| |
| tcu::ConstPixelBufferAccess faces0[CUBEFACE_LAST]; |
| tcu::ConstPixelBufferAccess faces1[CUBEFACE_LAST]; |
| |
| for (int face = 0; face < CUBEFACE_LAST; face++) |
| { |
| faces0[face] = texture.getLevelFace(baseLevelNdx, CubeFace(face)); |
| faces1[face] = texture.getLevelFace(baseLevelNdx+1, CubeFace(face)); |
| } |
| |
| for (int j0 = minJ0; j0 <= maxJ0; j0++) |
| { |
| for (int i0 = minI0; i0 <= maxI0; i0++) |
| { |
| const float minA0 = de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f); |
| const float maxA0 = de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f); |
| const float minB0 = de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f); |
| const float maxB0 = de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f); |
| Vec4 depths0; |
| |
| { |
| const CubeFaceIntCoords c00 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+0)), size0); |
| const CubeFaceIntCoords c10 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+0)), size0); |
| const CubeFaceIntCoords c01 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+1)), size0); |
| const CubeFaceIntCoords c11 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+1)), size0); |
| |
| // If any of samples is out of both edges, implementations can do pretty much anything according to spec. |
| // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. |
| if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST) |
| return true; |
| |
| depths0[0] = lookupDepthNoBorder(faces0[c00.face], sampler, c00.s, c00.t); |
| depths0[1] = lookupDepthNoBorder(faces0[c10.face], sampler, c10.s, c10.t); |
| depths0[2] = lookupDepthNoBorder(faces0[c01.face], sampler, c01.s, c01.t); |
| depths0[3] = lookupDepthNoBorder(faces0[c11.face], sampler, c11.s, c11.t); |
| } |
| |
| for (int j1 = minJ1; j1 <= maxJ1; j1++) |
| { |
| for (int i1 = minI1; i1 <= maxI1; i1++) |
| { |
| const float minA1 = de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f); |
| const float maxA1 = de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f); |
| const float minB1 = de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f); |
| const float maxB1 = de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f); |
| Vec4 depths1; |
| |
| { |
| const CubeFaceIntCoords c00 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+0)), size1); |
| const CubeFaceIntCoords c10 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+0)), size1); |
| const CubeFaceIntCoords c01 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+1)), size1); |
| const CubeFaceIntCoords c11 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+1)), size1); |
| |
| if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST) |
| return true; |
| |
| depths1[0] = lookupDepthNoBorder(faces1[c00.face], sampler, c00.s, c00.t); |
| depths1[1] = lookupDepthNoBorder(faces1[c10.face], sampler, c10.s, c10.t); |
| depths1[2] = lookupDepthNoBorder(faces1[c01.face], sampler, c01.s, c01.t); |
| depths1[3] = lookupDepthNoBorder(faces1[c11.face], sampler, c11.s, c11.t); |
| } |
| |
| |
| if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, |
| Vec2(minA0, maxA0), Vec2(minB0, maxB0), |
| Vec2(minA1, maxA1), Vec2(minB1, maxB1), |
| fBounds, cmpReference, result, isFixedPointDepth)) |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isCubeMipmapLinearCompareResultValid (const TextureCubeView& texture, |
| const int baseLevelNdx, |
| const Sampler& sampler, |
| const Sampler::FilterMode levelFilter, |
| const TexComparePrecision& prec, |
| const CubeFaceFloatCoords& coords, |
| const Vec2& fBounds, |
| const float cmpReference, |
| const float result) |
| { |
| if (levelFilter == Sampler::LINEAR) |
| { |
| if (sampler.seamlessCubeMap) |
| return isSeamplessLinearMipmapLinearCompareResultValid(texture, baseLevelNdx, sampler, prec, coords, fBounds, cmpReference, result); |
| else |
| return isLinearMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx, coords.face), |
| texture.getLevelFace(baseLevelNdx+1, coords.face), |
| sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result); |
| } |
| else |
| return isNearestMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx, coords.face), |
| texture.getLevelFace(baseLevelNdx+1, coords.face), |
| sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result); |
| } |
| |
| static bool isSeamlessLinearCompareResultValid (const TextureCubeView& texture, |
| const int levelNdx, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const CubeFaceFloatCoords& coords, |
| const float cmpReference, |
| const float result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(levelNdx, CUBEFACE_NEGATIVE_X).getFormat()); |
| const int size = texture.getLevelFace(levelNdx, coords.face).getWidth(); |
| |
| const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinate bounds for (x0,y0) - without wrap mode |
| const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); |
| const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); |
| const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); |
| const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); |
| |
| // Face accesses |
| ConstPixelBufferAccess faces[CUBEFACE_LAST]; |
| for (int face = 0; face < CUBEFACE_LAST; face++) |
| faces[face] = texture.getLevelFace(levelNdx, CubeFace(face)); |
| |
| for (int j = minJ; j <= maxJ; j++) |
| { |
| for (int i = minI; i <= maxI; i++) |
| { |
| const CubeFaceIntCoords c00 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+0)), size); |
| const CubeFaceIntCoords c10 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+0)), size); |
| const CubeFaceIntCoords c01 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+1)), size); |
| const CubeFaceIntCoords c11 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+1)), size); |
| |
| // If any of samples is out of both edges, implementations can do pretty much anything according to spec. |
| // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. |
| if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST) |
| return true; |
| |
| // Bounds for filtering factors |
| const float minA = de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f); |
| const float maxA = de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f); |
| const float minB = de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f); |
| const float maxB = de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f); |
| |
| Vec4 depths; |
| depths[0] = lookupDepthNoBorder(faces[c00.face], sampler, c00.s, c00.t); |
| depths[1] = lookupDepthNoBorder(faces[c10.face], sampler, c10.s, c10.t); |
| depths[2] = lookupDepthNoBorder(faces[c01.face], sampler, c01.s, c01.t); |
| depths[3] = lookupDepthNoBorder(faces[c11.face], sampler, c11.s, c11.t); |
| |
| if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isCubeLevelCompareResultValid (const TextureCubeView& texture, |
| const int levelNdx, |
| const Sampler& sampler, |
| const Sampler::FilterMode filterMode, |
| const TexComparePrecision& prec, |
| const CubeFaceFloatCoords& coords, |
| const float cmpReference, |
| const float result) |
| { |
| if (filterMode == Sampler::LINEAR) |
| { |
| if (sampler.seamlessCubeMap) |
| return isSeamlessLinearCompareResultValid(texture, levelNdx, sampler, prec, coords, cmpReference, result); |
| else |
| return isLinearCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result); |
| } |
| else |
| return isNearestCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result); |
| } |
| |
| bool isTexCompareResultValid (const TextureCubeView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result) |
| { |
| int numPossibleFaces = 0; |
| CubeFace possibleFaces[CUBEFACE_LAST]; |
| |
| DE_ASSERT(isSamplerSupported(sampler)); |
| |
| getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces); |
| |
| if (numPossibleFaces == 0) |
| return true; // Result is undefined. |
| |
| for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++) |
| { |
| const CubeFaceFloatCoords faceCoords (possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord)); |
| const float minLod = lodBounds.x(); |
| const float maxLod = lodBounds.y(); |
| const bool canBeMagnified = minLod <= sampler.lodThreshold; |
| const bool canBeMinified = maxLod > sampler.lodThreshold; |
| |
| if (canBeMagnified) |
| { |
| if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, cmpReference, result)) |
| return true; |
| } |
| |
| if (canBeMinified) |
| { |
| const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter); |
| const bool isLinearMipmap = isLinearMipmapFilter(sampler.minFilter); |
| const int minTexLevel = 0; |
| const int maxTexLevel = texture.getNumLevels()-1; |
| |
| DE_ASSERT(minTexLevel < maxTexLevel); |
| |
| if (isLinearMipmap) |
| { |
| const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1); |
| const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1); |
| |
| DE_ASSERT(minLevel <= maxLevel); |
| |
| for (int level = minLevel; level <= maxLevel; level++) |
| { |
| const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f); |
| const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f); |
| |
| if (isCubeMipmapLinearCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), cmpReference, result)) |
| return true; |
| } |
| } |
| else if (isNearestMipmap) |
| { |
| // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made |
| // decision to allow floor(lod + 0.5) as well. |
| const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel); |
| const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel); |
| |
| DE_ASSERT(minLevel <= maxLevel); |
| |
| for (int level = minLevel; level <= maxLevel; level++) |
| { |
| if (isCubeLevelCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, cmpReference, result)) |
| return true; |
| } |
| } |
| else |
| { |
| if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords, cmpReference, result)) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool isTexCompareResultValid (const Texture2DArrayView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result) |
| { |
| const float depthErr = computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z()); |
| const float minZ = coord.z()-depthErr; |
| const float maxZ = coord.z()+depthErr; |
| const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1); |
| const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1); |
| |
| DE_ASSERT(isSamplerSupported(sampler)); |
| |
| for (int layer = minLayer; layer <= maxLayer; layer++) |
| { |
| const float minLod = lodBounds.x(); |
| const float maxLod = lodBounds.y(); |
| const bool canBeMagnified = minLod <= sampler.lodThreshold; |
| const bool canBeMinified = maxLod > sampler.lodThreshold; |
| |
| if (canBeMagnified) |
| { |
| if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord.swizzle(0,1), layer, cmpReference, result)) |
| return true; |
| } |
| |
| if (canBeMinified) |
| { |
| const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter); |
| const bool isLinearMipmap = isLinearMipmapFilter(sampler.minFilter); |
| const int minTexLevel = 0; |
| const int maxTexLevel = texture.getNumLevels()-1; |
| |
| DE_ASSERT(minTexLevel < maxTexLevel); |
| |
| if (isLinearMipmap) |
| { |
| const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1); |
| const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1); |
| |
| DE_ASSERT(minLevel <= maxLevel); |
| |
| for (int level = minLevel; level <= maxLevel; level++) |
| { |
| const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f); |
| const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f); |
| |
| if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, Vec2(minF, maxF), cmpReference, result)) |
| return true; |
| } |
| } |
| else if (isNearestMipmap) |
| { |
| // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made |
| // decision to allow floor(lod + 0.5) as well. |
| const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel); |
| const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel); |
| |
| DE_ASSERT(minLevel <= maxLevel); |
| |
| for (int level = minLevel; level <= maxLevel; level++) |
| { |
| if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, cmpReference, result)) |
| return true; |
| } |
| } |
| else |
| { |
| if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord.swizzle(0,1), layer, cmpReference, result)) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isGatherOffsetsCompareResultValid (const ConstPixelBufferAccess& texture, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| int coordZ, |
| const IVec2 (&offsets)[4], |
| float cmpReference, |
| const Vec4& result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getFormat()); |
| const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinate bounds for (x0, y0) - without wrap mode |
| const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); |
| const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); |
| const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); |
| const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); |
| |
| const int w = texture.getWidth(); |
| const int h = texture.getHeight(); |
| |
| for (int j = minJ; j <= maxJ; j++) |
| { |
| for (int i = minI; i <= maxI; i++) |
| { |
| bool isCurrentPixelValid = true; |
| |
| for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++) |
| { |
| // offNdx-th coordinate offset and then wrapped. |
| const int x = wrap(sampler.wrapS, i+offsets[offNdx].x(), w); |
| const int y = wrap(sampler.wrapT, j+offsets[offNdx].y(), h); |
| const float depth = lookupDepth(texture, sampler, x, y, coordZ); |
| const CmpResultSet resSet = execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| if (!isResultInSet(resSet, result[offNdx], prec.resultBits)) |
| isCurrentPixelValid = false; |
| } |
| |
| if (isCurrentPixelValid) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool isGatherOffsetsCompareResultValid (const Texture2DView& texture, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec2& coord, |
| const IVec2 (&offsets)[4], |
| float cmpReference, |
| const Vec4& result) |
| { |
| DE_ASSERT(isSamplerSupported(sampler)); |
| |
| return isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord, 0, offsets, cmpReference, result); |
| } |
| |
| bool isGatherOffsetsCompareResultValid (const Texture2DArrayView& texture, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec3& coord, |
| const IVec2 (&offsets)[4], |
| float cmpReference, |
| const Vec4& result) |
| { |
| const float depthErr = computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z()); |
| const float minZ = coord.z()-depthErr; |
| const float maxZ = coord.z()+depthErr; |
| const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1); |
| const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1); |
| |
| DE_ASSERT(isSamplerSupported(sampler)); |
| |
| for (int layer = minLayer; layer <= maxLayer; layer++) |
| { |
| if (isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0,1), layer, offsets, cmpReference, result)) |
| return true; |
| } |
| return false; |
| } |
| |
| static bool isGatherCompareResultValid (const TextureCubeView& texture, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const CubeFaceFloatCoords& coords, |
| float cmpReference, |
| const Vec4& result) |
| { |
| const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(0, coords.face).getFormat()); |
| const int size = texture.getLevelFace(0, coords.face).getWidth(); |
| const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x()); |
| const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y()); |
| |
| // Integer coordinate bounds for (x0,y0) - without wrap mode |
| const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); |
| const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); |
| const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); |
| const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); |
| |
| // Face accesses |
| ConstPixelBufferAccess faces[CUBEFACE_LAST]; |
| for (int face = 0; face < CUBEFACE_LAST; face++) |
| faces[face] = texture.getLevelFace(0, CubeFace(face)); |
| |
| for (int j = minJ; j <= maxJ; j++) |
| { |
| for (int i = minI; i <= maxI; i++) |
| { |
| static const IVec2 offsets[4] = |
| { |
| IVec2(0, 1), |
| IVec2(1, 1), |
| IVec2(1, 0), |
| IVec2(0, 0) |
| }; |
| |
| bool isCurrentPixelValid = true; |
| |
| for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++) |
| { |
| const CubeFaceIntCoords c = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, i+offsets[offNdx].x(), j+offsets[offNdx].y()), size); |
| // If any of samples is out of both edges, implementations can do pretty much anything according to spec. |
| // \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color. |
| // See also isSeamlessLinearCompareResultValid and similar. |
| if (c.face == CUBEFACE_LAST) |
| return true; |
| |
| const float depth = lookupDepthNoBorder(faces[c.face], sampler, c.s, c.t); |
| const CmpResultSet resSet = execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); |
| |
| if (!isResultInSet(resSet, result[offNdx], prec.resultBits)) |
| isCurrentPixelValid = false; |
| } |
| |
| if (isCurrentPixelValid) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool isGatherCompareResultValid (const TextureCubeView& texture, |
| const Sampler& sampler, |
| const TexComparePrecision& prec, |
| const Vec3& coord, |
| float cmpReference, |
| const Vec4& result) |
| { |
| int numPossibleFaces = 0; |
| CubeFace possibleFaces[CUBEFACE_LAST]; |
| |
| DE_ASSERT(isSamplerSupported(sampler)); |
| |
| getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces); |
| |
| if (numPossibleFaces == 0) |
| return true; // Result is undefined. |
| |
| for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++) |
| { |
| const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord)); |
| |
| if (isGatherCompareResultValid(texture, sampler, prec, faceCoords, cmpReference, result)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // tcu |