Home | History | Annotate | Download | only in common
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Tester Core
      3  * ----------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Texture compare (shadow) result verifier.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "tcuTexCompareVerifier.hpp"
     25 #include "tcuTexVerifierUtil.hpp"
     26 #include "tcuTextureUtil.hpp"
     27 #include "tcuVectorUtil.hpp"
     28 #include "deMath.h"
     29 
     30 namespace tcu
     31 {
     32 
     33 using namespace TexVerifierUtil;
     34 
     35 // Generic utilities
     36 
     37 #if defined(DE_DEBUG)
     38 static bool isSamplerSupported (const Sampler& sampler)
     39 {
     40 	return sampler.compare != Sampler::COMPAREMODE_NONE &&
     41 		   isWrapModeSupported(sampler.wrapS)			&&
     42 		   isWrapModeSupported(sampler.wrapT)			&&
     43 		   isWrapModeSupported(sampler.wrapR);
     44 }
     45 #endif // DE_DEBUG
     46 
     47 struct CmpResultSet
     48 {
     49 	bool	isTrue;
     50 	bool	isFalse;
     51 
     52 	CmpResultSet (void)
     53 		: isTrue	(false)
     54 		, isFalse	(false)
     55 	{
     56 	}
     57 };
     58 
     59 static CmpResultSet execCompare (const Sampler::CompareMode	compareMode,
     60 								 const float				cmpValue_,
     61 								 const float				cmpReference_,
     62 								 const int					referenceBits,
     63 								 const bool					isFixedPoint)
     64 {
     65 	const bool		clampValues		= isFixedPoint;	// if comparing against a floating point texture, ref (and value) is not clamped
     66 	const float		cmpValue		= (clampValues) ? (de::clamp(cmpValue_, 0.0f, 1.0f)) : (cmpValue_);
     67 	const float		cmpReference	= (clampValues) ? (de::clamp(cmpReference_, 0.0f, 1.0f)) : (cmpReference_);
     68 	const float		err				= computeFixedPointError(referenceBits);
     69 	CmpResultSet	res;
     70 
     71 	switch (compareMode)
     72 	{
     73 		case Sampler::COMPAREMODE_LESS:
     74 			res.isTrue	= cmpReference-err < cmpValue;
     75 			res.isFalse	= cmpReference+err >= cmpValue;
     76 			break;
     77 
     78 		case Sampler::COMPAREMODE_LESS_OR_EQUAL:
     79 			res.isTrue	= cmpReference-err <= cmpValue;
     80 			res.isFalse	= cmpReference+err > cmpValue;
     81 			break;
     82 
     83 		case Sampler::COMPAREMODE_GREATER:
     84 			res.isTrue	= cmpReference+err > cmpValue;
     85 			res.isFalse	= cmpReference-err <= cmpValue;
     86 			break;
     87 
     88 		case Sampler::COMPAREMODE_GREATER_OR_EQUAL:
     89 			res.isTrue	= cmpReference+err >= cmpValue;
     90 			res.isFalse	= cmpReference-err < cmpValue;
     91 			break;
     92 
     93 		case Sampler::COMPAREMODE_EQUAL:
     94 			res.isTrue	= de::inRange(cmpValue, cmpReference-err, cmpReference+err);
     95 			res.isFalse	= err != 0.0f || cmpValue != cmpReference;
     96 			break;
     97 
     98 		case Sampler::COMPAREMODE_NOT_EQUAL:
     99 			res.isTrue	= err != 0.0f || cmpValue != cmpReference;
    100 			res.isFalse	= de::inRange(cmpValue, cmpReference-err, cmpReference+err);
    101 			break;
    102 
    103 		case Sampler::COMPAREMODE_ALWAYS:
    104 			res.isTrue	= true;
    105 			break;
    106 
    107 		case Sampler::COMPAREMODE_NEVER:
    108 			res.isFalse	= true;
    109 			break;
    110 
    111 		default:
    112 			DE_ASSERT(false);
    113 	}
    114 
    115 	DE_ASSERT(res.isTrue || res.isFalse);
    116 	return res;
    117 }
    118 
    119 static inline bool isResultInSet (const CmpResultSet resultSet, const float result, const int resultBits)
    120 {
    121 	const float err		= computeFixedPointError(resultBits);
    122 	const float	minR	= result-err;
    123 	const float	maxR	= result+err;
    124 
    125 	return (resultSet.isTrue	&& de::inRange(1.0f, minR, maxR)) ||
    126 		   (resultSet.isFalse	&& de::inRange(0.0f, minR, maxR));
    127 }
    128 
    129 static inline bool coordsInBounds (const ConstPixelBufferAccess& access, int x, int y, int z)
    130 {
    131 	return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) && de::inBounds(z, 0, access.getDepth());
    132 }
    133 
    134 static float lookupDepth (const tcu::ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k)
    135 {
    136 	if (coordsInBounds(access, i, j, k))
    137 		return access.getPixDepth(i, j, k);
    138 	else
    139 		return sampleTextureBorder<float>(access.getFormat(), sampler).x();
    140 }
    141 
    142 // lookup depth value at a point that is guaranteed to not sample border such as cube map faces.
    143 static float lookupDepthNoBorder (const tcu::ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k = 0)
    144 {
    145 	DE_UNREF(sampler);
    146 	DE_ASSERT(coordsInBounds(access, i, j, k));
    147 	return access.getPixDepth(i, j, k);
    148 }
    149 
    150 // Values are in order (0,0), (1,0), (0,1), (1,1)
    151 static float bilinearInterpolate (const Vec4& values, const float x, const float y)
    152 {
    153 	const float		v00		= values[0];
    154 	const float		v10		= values[1];
    155 	const float		v01		= values[2];
    156 	const float		v11		= values[3];
    157 	const float		res		= v00*(1.0f-x)*(1.0f-y) + v10*x*(1.0f-y) + v01*(1.0f-x)*y + v11*x*y;
    158 	return res;
    159 }
    160 
    161 static bool isFixedPointDepthTextureFormat (const tcu::TextureFormat& format)
    162 {
    163 	const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(format.type);
    164 
    165 	if (format.order == TextureFormat::D)
    166 	{
    167 		// depth internal formats cannot be non-normalized integers
    168 		return channelClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT;
    169 	}
    170 	else if (format.order == TextureFormat::DS)
    171 	{
    172 		// combined formats have no single channel class, detect format manually
    173 		switch (format.type)
    174 		{
    175 			case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return false;
    176 			case tcu::TextureFormat::UNSIGNED_INT_24_8:				return true;
    177 
    178 			default:
    179 			{
    180 				// unknown format
    181 				DE_ASSERT(false);
    182 				return true;
    183 			}
    184 		}
    185 	}
    186 
    187 	return false;
    188 }
    189 
    190 static bool isLinearCompareValid (const Sampler::CompareMode	compareMode,
    191 								  const TexComparePrecision&	prec,
    192 								  const Vec2&					depths,
    193 								  const Vec2&					fBounds,
    194 								  const float					cmpReference,
    195 								  const float					result,
    196 								  const bool					isFixedPointDepth)
    197 {
    198 	DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f);
    199 
    200 	const float			d0			= depths[0];
    201 	const float			d1			= depths[1];
    202 
    203 	const CmpResultSet	cmp0		= execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
    204 	const CmpResultSet	cmp1		= execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
    205 
    206 	const deUint32		isTrue		= (deUint32(cmp0.isTrue)<<0)
    207 									| (deUint32(cmp1.isTrue)<<1);
    208 	const deUint32		isFalse		= (deUint32(cmp0.isFalse)<<0)
    209 									| (deUint32(cmp1.isFalse)<<1);
    210 
    211 	// Interpolation parameters
    212 	const float			f0			= fBounds.x();
    213 	const float			f1			= fBounds.y();
    214 
    215 	// Error parameters
    216 	const float			pcfErr		= computeFixedPointError(prec.pcfBits);
    217 	const float			resErr		= computeFixedPointError(prec.resultBits);
    218 	const float			totalErr	= pcfErr+resErr;
    219 
    220 	// Iterate over all valid combinations.
    221 	for (deUint32 comb = 0; comb < (1<<2); comb++)
    222 	{
    223 		// Filter out invalid combinations.
    224 		if (((comb & isTrue) | (~comb & isFalse)) != (1<<2)-1)
    225 			continue;
    226 
    227 		const bool		cmp0True	= ((comb>>0)&1) != 0;
    228 		const bool		cmp1True	= ((comb>>1)&1) != 0;
    229 
    230 		const float		ref0		= cmp0True ? 1.0f : 0.0f;
    231 		const float		ref1		= cmp1True ? 1.0f : 0.0f;
    232 
    233 		const float		v0			= ref0*(1.0f-f0) + ref1*f0;
    234 		const float		v1			= ref0*(1.0f-f1) + ref1*f1;
    235 		const float		minV		= de::min(v0, v1);
    236 		const float		maxV		= de::max(v0, v1);
    237 		const float		minR		= minV-totalErr;
    238 		const float		maxR		= maxV+totalErr;
    239 
    240 		if (de::inRange(result, minR, maxR))
    241 			return true;
    242 	}
    243 
    244 	return false;
    245 }
    246 
    247 static inline BVec4 extractBVec4 (const deUint32 val, int offset)
    248 {
    249 	return BVec4(((val>>(offset+0))&1) != 0,
    250 				 ((val>>(offset+1))&1) != 0,
    251 				 ((val>>(offset+2))&1) != 0,
    252 				 ((val>>(offset+3))&1) != 0);
    253 }
    254 
    255 static bool isBilinearAnyCompareValid (const Sampler::CompareMode	compareMode,
    256 									   const TexComparePrecision&	prec,
    257 									   const Vec4&					depths,
    258 									   const float					cmpReference,
    259 									   const float					result,
    260 									   const bool					isFixedPointDepth)
    261 {
    262 	DE_ASSERT(prec.pcfBits == 0);
    263 
    264 	const float			d0			= depths[0];
    265 	const float			d1			= depths[1];
    266 	const float			d2			= depths[2];
    267 	const float			d3			= depths[3];
    268 
    269 	const CmpResultSet	cmp0		= execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
    270 	const CmpResultSet	cmp1		= execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
    271 	const CmpResultSet	cmp2		= execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth);
    272 	const CmpResultSet	cmp3		= execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth);
    273 
    274 	const bool			canBeTrue	= cmp0.isTrue || cmp1.isTrue || cmp2.isTrue || cmp3.isTrue;
    275 	const bool			canBeFalse	= cmp0.isFalse || cmp1.isFalse || cmp2.isFalse || cmp3.isFalse;
    276 
    277 	const float			resErr		= computeFixedPointError(prec.resultBits);
    278 
    279 	const float			minBound	= canBeFalse ? 0.0f : 1.0f;
    280 	const float			maxBound	= canBeTrue ? 1.0f : 0.0f;
    281 
    282 	return de::inRange(result, minBound-resErr, maxBound+resErr);
    283 }
    284 
    285 static bool isBilinearPCFCompareValid (const Sampler::CompareMode	compareMode,
    286 									   const TexComparePrecision&	prec,
    287 									   const Vec4&					depths,
    288 									   const Vec2&					xBounds,
    289 									   const Vec2&					yBounds,
    290 									   const float					cmpReference,
    291 									   const float					result,
    292 									   const bool					isFixedPointDepth)
    293 {
    294 	DE_ASSERT(0.0f <= xBounds.x() && xBounds.x() <= xBounds.y() && xBounds.y() <= 1.0f);
    295 	DE_ASSERT(0.0f <= yBounds.x() && yBounds.x() <= yBounds.y() && yBounds.y() <= 1.0f);
    296 	DE_ASSERT(prec.pcfBits > 0);
    297 
    298 	const float			d0			= depths[0];
    299 	const float			d1			= depths[1];
    300 	const float			d2			= depths[2];
    301 	const float			d3			= depths[3];
    302 
    303 	const CmpResultSet	cmp0		= execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
    304 	const CmpResultSet	cmp1		= execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
    305 	const CmpResultSet	cmp2		= execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth);
    306 	const CmpResultSet	cmp3		= execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth);
    307 
    308 	const deUint32		isTrue		= (deUint32(cmp0.isTrue)<<0)
    309 									| (deUint32(cmp1.isTrue)<<1)
    310 									| (deUint32(cmp2.isTrue)<<2)
    311 									| (deUint32(cmp3.isTrue)<<3);
    312 	const deUint32		isFalse		= (deUint32(cmp0.isFalse)<<0)
    313 									| (deUint32(cmp1.isFalse)<<1)
    314 									| (deUint32(cmp2.isFalse)<<2)
    315 									| (deUint32(cmp3.isFalse)<<3);
    316 
    317 	// Interpolation parameters
    318 	const float			x0			= xBounds.x();
    319 	const float			x1			= xBounds.y();
    320 	const float			y0			= yBounds.x();
    321 	const float			y1			= yBounds.y();
    322 
    323 	// Error parameters
    324 	const float			pcfErr		= computeFixedPointError(prec.pcfBits);
    325 	const float			resErr		= computeFixedPointError(prec.resultBits);
    326 	const float			totalErr	= pcfErr+resErr;
    327 
    328 	// Iterate over all valid combinations.
    329 	// \note It is not enough to compute minmax over all possible result sets, as ranges may
    330 	//		 not necessarily overlap, i.e. there are gaps between valid ranges.
    331 	for (deUint32 comb = 0; comb < (1<<4); comb++)
    332 	{
    333 		// Filter out invalid combinations:
    334 		//  1) True bit is set in comb but not in isTrue => sample can not be true
    335 		//  2) True bit is NOT set in comb and not in isFalse => sample can not be false
    336 		if (((comb & isTrue) | (~comb & isFalse)) != (1<<4)-1)
    337 			continue;
    338 
    339 		const BVec4		cmpTrue		= extractBVec4(comb, 0);
    340 		const Vec4		refVal		= select(Vec4(1.0f), Vec4(0.0f), cmpTrue);
    341 
    342 		const float		v0			= bilinearInterpolate(refVal, x0, y0);
    343 		const float		v1			= bilinearInterpolate(refVal, x1, y0);
    344 		const float		v2			= bilinearInterpolate(refVal, x0, y1);
    345 		const float		v3			= bilinearInterpolate(refVal, x1, y1);
    346 		const float		minV		= de::min(v0, de::min(v1, de::min(v2, v3)));
    347 		const float		maxV		= de::max(v0, de::max(v1, de::max(v2, v3)));
    348 		const float		minR		= minV-totalErr;
    349 		const float		maxR		= maxV+totalErr;
    350 
    351 		if (de::inRange(result, minR, maxR))
    352 			return true;
    353 	}
    354 
    355 	return false;
    356 }
    357 
    358 static bool isBilinearCompareValid (const Sampler::CompareMode	compareMode,
    359 									const TexComparePrecision&	prec,
    360 									const Vec4&					depths,
    361 									const Vec2&					xBounds,
    362 									const Vec2&					yBounds,
    363 									const float					cmpReference,
    364 									const float					result,
    365 									const bool					isFixedPointDepth)
    366 {
    367 	if (prec.pcfBits > 0)
    368 		return isBilinearPCFCompareValid(compareMode, prec, depths, xBounds, yBounds, cmpReference, result, isFixedPointDepth);
    369 	else
    370 		return isBilinearAnyCompareValid(compareMode, prec, depths, cmpReference, result, isFixedPointDepth);
    371 }
    372 
    373 static bool isTrilinearAnyCompareValid (const Sampler::CompareMode	compareMode,
    374 										const TexComparePrecision&	prec,
    375 										const Vec4&					depths0,
    376 										const Vec4&					depths1,
    377 										const float					cmpReference,
    378 										const float					result,
    379 										const bool					isFixedPointDepth)
    380 {
    381 	DE_ASSERT(prec.pcfBits == 0);
    382 
    383 	const CmpResultSet	cmp00		= execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth);
    384 	const CmpResultSet	cmp01		= execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth);
    385 	const CmpResultSet	cmp02		= execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth);
    386 	const CmpResultSet	cmp03		= execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth);
    387 
    388 	const CmpResultSet	cmp10		= execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth);
    389 	const CmpResultSet	cmp11		= execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth);
    390 	const CmpResultSet	cmp12		= execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth);
    391 	const CmpResultSet	cmp13		= execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth);
    392 
    393 	const bool			canBeTrue	= cmp00.isTrue ||
    394 									  cmp01.isTrue ||
    395 									  cmp02.isTrue ||
    396 									  cmp03.isTrue ||
    397 									  cmp10.isTrue ||
    398 									  cmp11.isTrue ||
    399 									  cmp12.isTrue ||
    400 									  cmp13.isTrue;
    401 	const bool			canBeFalse	= cmp00.isFalse ||
    402 									  cmp01.isFalse ||
    403 									  cmp02.isFalse ||
    404 									  cmp03.isFalse ||
    405 									  cmp10.isFalse ||
    406 									  cmp11.isFalse ||
    407 									  cmp12.isFalse ||
    408 									  cmp13.isFalse;
    409 
    410 	const float			resErr		= computeFixedPointError(prec.resultBits);
    411 
    412 	const float			minBound	= canBeFalse ? 0.0f : 1.0f;
    413 	const float			maxBound	= canBeTrue ? 1.0f : 0.0f;
    414 
    415 	return de::inRange(result, minBound-resErr, maxBound+resErr);
    416 }
    417 
    418 static bool isTrilinearPCFCompareValid (const Sampler::CompareMode	compareMode,
    419 										const TexComparePrecision&	prec,
    420 										const Vec4&					depths0,
    421 										const Vec4&					depths1,
    422 										const Vec2&					xBounds0,
    423 										const Vec2&					yBounds0,
    424 										const Vec2&					xBounds1,
    425 										const Vec2&					yBounds1,
    426 										const Vec2&					fBounds,
    427 										const float					cmpReference,
    428 										const float					result,
    429 										const bool					isFixedPointDepth)
    430 {
    431 	DE_ASSERT(0.0f <= xBounds0.x() && xBounds0.x() <= xBounds0.y() && xBounds0.y() <= 1.0f);
    432 	DE_ASSERT(0.0f <= yBounds0.x() && yBounds0.x() <= yBounds0.y() && yBounds0.y() <= 1.0f);
    433 	DE_ASSERT(0.0f <= xBounds1.x() && xBounds1.x() <= xBounds1.y() && xBounds1.y() <= 1.0f);
    434 	DE_ASSERT(0.0f <= yBounds1.x() && yBounds1.x() <= yBounds1.y() && yBounds1.y() <= 1.0f);
    435 	DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f);
    436 	DE_ASSERT(prec.pcfBits > 0);
    437 
    438 	const CmpResultSet	cmp00		= execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth);
    439 	const CmpResultSet	cmp01		= execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth);
    440 	const CmpResultSet	cmp02		= execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth);
    441 	const CmpResultSet	cmp03		= execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth);
    442 
    443 	const CmpResultSet	cmp10		= execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth);
    444 	const CmpResultSet	cmp11		= execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth);
    445 	const CmpResultSet	cmp12		= execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth);
    446 	const CmpResultSet	cmp13		= execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth);
    447 
    448 	const deUint32		isTrue		= (deUint32(cmp00.isTrue)<<0)
    449 									| (deUint32(cmp01.isTrue)<<1)
    450 									| (deUint32(cmp02.isTrue)<<2)
    451 									| (deUint32(cmp03.isTrue)<<3)
    452 									| (deUint32(cmp10.isTrue)<<4)
    453 									| (deUint32(cmp11.isTrue)<<5)
    454 									| (deUint32(cmp12.isTrue)<<6)
    455 									| (deUint32(cmp13.isTrue)<<7);
    456 	const deUint32		isFalse		= (deUint32(cmp00.isFalse)<<0)
    457 									| (deUint32(cmp01.isFalse)<<1)
    458 									| (deUint32(cmp02.isFalse)<<2)
    459 									| (deUint32(cmp03.isFalse)<<3)
    460 									| (deUint32(cmp10.isFalse)<<4)
    461 									| (deUint32(cmp11.isFalse)<<5)
    462 									| (deUint32(cmp12.isFalse)<<6)
    463 									| (deUint32(cmp13.isFalse)<<7);
    464 
    465 	// Error parameters
    466 	const float			pcfErr		= computeFixedPointError(prec.pcfBits);
    467 	const float			resErr		= computeFixedPointError(prec.resultBits);
    468 	const float			totalErr	= pcfErr+resErr;
    469 
    470 	// Iterate over all valid combinations.
    471 	for (deUint32 comb = 0; comb < (1<<8); comb++)
    472 	{
    473 		// Filter out invalid combinations.
    474 		if (((comb & isTrue) | (~comb & isFalse)) != (1<<8)-1)
    475 			continue;
    476 
    477 		const BVec4		cmpTrue0	= extractBVec4(comb, 0);
    478 		const BVec4		cmpTrue1	= extractBVec4(comb, 4);
    479 		const Vec4		refVal0		= select(Vec4(1.0f), Vec4(0.0f), cmpTrue0);
    480 		const Vec4		refVal1		= select(Vec4(1.0f), Vec4(0.0f), cmpTrue1);
    481 
    482 		// Bilinear interpolation within levels.
    483 		const float		v00			= bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.x());
    484 		const float		v01			= bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.x());
    485 		const float		v02			= bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.y());
    486 		const float		v03			= bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.y());
    487 		const float		minV0		= de::min(v00, de::min(v01, de::min(v02, v03)));
    488 		const float		maxV0		= de::max(v00, de::max(v01, de::max(v02, v03)));
    489 
    490 		const float		v10			= bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.x());
    491 		const float		v11			= bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.x());
    492 		const float		v12			= bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.y());
    493 		const float		v13			= bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.y());
    494 		const float		minV1		= de::min(v10, de::min(v11, de::min(v12, v13)));
    495 		const float		maxV1		= de::max(v10, de::max(v11, de::max(v12, v13)));
    496 
    497 		// Compute min-max bounds by filtering between minimum bounds and maximum bounds between levels.
    498 		// HW can end up choosing pretty much any of samples between levels, and thus interpolating
    499 		// between minimums should yield lower bound for range, and same for upper bound.
    500 		// \todo [2013-07-17 pyry] This seems separable? Can this be optimized? At least ranges could be pre-computed and later combined.
    501 		const float		minF0		= minV0*(1.0f-fBounds.x()) + minV1*fBounds.x();
    502 		const float		minF1		= minV0*(1.0f-fBounds.y()) + minV1*fBounds.y();
    503 		const float		maxF0		= maxV0*(1.0f-fBounds.x()) + maxV1*fBounds.x();
    504 		const float		maxF1		= maxV0*(1.0f-fBounds.y()) + maxV1*fBounds.y();
    505 
    506 		const float		minF		= de::min(minF0, minF1);
    507 		const float		maxF		= de::max(maxF0, maxF1);
    508 
    509 		const float		minR		= minF-totalErr;
    510 		const float		maxR		= maxF+totalErr;
    511 
    512 		if (de::inRange(result, minR, maxR))
    513 			return true;
    514 	}
    515 
    516 	return false;
    517 }
    518 
    519 static bool isTrilinearCompareValid (const Sampler::CompareMode	compareMode,
    520 									 const TexComparePrecision&	prec,
    521 									 const Vec4&				depths0,
    522 									 const Vec4&				depths1,
    523 									 const Vec2&				xBounds0,
    524 									 const Vec2&				yBounds0,
    525 									 const Vec2&				xBounds1,
    526 									 const Vec2&				yBounds1,
    527 									 const Vec2&				fBounds,
    528 									 const float				cmpReference,
    529 									 const float				result,
    530 									 const bool					isFixedPointDepth)
    531 {
    532 	if (prec.pcfBits > 0)
    533 		return isTrilinearPCFCompareValid(compareMode, prec, depths0, depths1, xBounds0, yBounds0, xBounds1, yBounds1, fBounds, cmpReference, result, isFixedPointDepth);
    534 	else
    535 		return isTrilinearAnyCompareValid(compareMode, prec, depths0, depths1, cmpReference, result, isFixedPointDepth);
    536 }
    537 
    538 static bool isNearestCompareResultValid (const ConstPixelBufferAccess&		level,
    539 										 const Sampler&						sampler,
    540 										 const TexComparePrecision&			prec,
    541 										 const Vec2&						coord,
    542 										 const int							coordZ,
    543 										 const float						cmpReference,
    544 										 const float						result)
    545 {
    546 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level.getFormat());
    547 	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    548 	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    549 
    550 	// Integer coordinates - without wrap mode
    551 	const int	minI		= deFloorFloatToInt32(uBounds.x());
    552 	const int	maxI		= deFloorFloatToInt32(uBounds.y());
    553 	const int	minJ		= deFloorFloatToInt32(vBounds.x());
    554 	const int	maxJ		= deFloorFloatToInt32(vBounds.y());
    555 
    556 	for (int j = minJ; j <= maxJ; j++)
    557 	{
    558 		for (int i = minI; i <= maxI; i++)
    559 		{
    560 			const int			x		= wrap(sampler.wrapS, i, level.getWidth());
    561 			const int			y		= wrap(sampler.wrapT, j, level.getHeight());
    562 			const float			depth	= lookupDepth(level, sampler, x, y, coordZ);
    563 			const CmpResultSet	resSet	= execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
    564 
    565 			if (isResultInSet(resSet, result, prec.resultBits))
    566 				return true;
    567 		}
    568 	}
    569 
    570 	return false;
    571 }
    572 
    573 static bool isLinearCompareResultValid (const ConstPixelBufferAccess&		level,
    574 										const Sampler&						sampler,
    575 										const TexComparePrecision&			prec,
    576 										const Vec2&							coord,
    577 										const int							coordZ,
    578 										const float							cmpReference,
    579 										const float							result)
    580 {
    581 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level.getFormat());
    582 	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    583 	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    584 
    585 	// Integer coordinate bounds for (x0,y0) - without wrap mode
    586 	const int	minI		= deFloorFloatToInt32(uBounds.x()-0.5f);
    587 	const int	maxI		= deFloorFloatToInt32(uBounds.y()-0.5f);
    588 	const int	minJ		= deFloorFloatToInt32(vBounds.x()-0.5f);
    589 	const int	maxJ		= deFloorFloatToInt32(vBounds.y()-0.5f);
    590 
    591 	const int	w			= level.getWidth();
    592 	const int	h			= level.getHeight();
    593 
    594 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    595 
    596 	for (int j = minJ; j <= maxJ; j++)
    597 	{
    598 		for (int i = minI; i <= maxI; i++)
    599 		{
    600 			// Wrapped coordinates
    601 			const int	x0		= wrap(sampler.wrapS, i  , w);
    602 			const int	x1		= wrap(sampler.wrapS, i+1, w);
    603 			const int	y0		= wrap(sampler.wrapT, j  , h);
    604 			const int	y1		= wrap(sampler.wrapT, j+1, h);
    605 
    606 			// Bounds for filtering factors
    607 			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    608 			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    609 			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
    610 			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
    611 
    612 			const Vec4	depths	(lookupDepth(level, sampler, x0, y0, coordZ),
    613 								 lookupDepth(level, sampler, x1, y0, coordZ),
    614 								 lookupDepth(level, sampler, x0, y1, coordZ),
    615 								 lookupDepth(level, sampler, x1, y1, coordZ));
    616 
    617 			if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth))
    618 				return true;
    619 		}
    620 	}
    621 
    622 	return false;
    623 }
    624 
    625 static bool isLevelCompareResultValid (const ConstPixelBufferAccess&	level,
    626 									   const Sampler&					sampler,
    627 									   const Sampler::FilterMode		filterMode,
    628 									   const TexComparePrecision&		prec,
    629 									   const Vec2&						coord,
    630 									   const int						coordZ,
    631 									   const float						cmpReference,
    632 									   const float						result)
    633 {
    634 	if (filterMode == Sampler::LINEAR)
    635 		return isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
    636 	else
    637 		return isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
    638 }
    639 
    640 static bool isNearestMipmapLinearCompareResultValid (const ConstPixelBufferAccess&	level0,
    641 													 const ConstPixelBufferAccess&	level1,
    642 													 const Sampler&					sampler,
    643 													 const TexComparePrecision&		prec,
    644 													 const Vec2&					coord,
    645 													 const int						coordZ,
    646 													 const Vec2&					fBounds,
    647 													 const float					cmpReference,
    648 													 const float					result)
    649 {
    650 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level0.getFormat());
    651 
    652 	const int	w0					= level0.getWidth();
    653 	const int	w1					= level1.getWidth();
    654 	const int	h0					= level0.getHeight();
    655 	const int	h1					= level1.getHeight();
    656 
    657 	const Vec2	uBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    658 	const Vec2	uBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    659 	const Vec2	vBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    660 	const Vec2	vBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    661 
    662 	// Integer coordinates - without wrap mode
    663 	const int	minI0				= deFloorFloatToInt32(uBounds0.x());
    664 	const int	maxI0				= deFloorFloatToInt32(uBounds0.y());
    665 	const int	minI1				= deFloorFloatToInt32(uBounds1.x());
    666 	const int	maxI1				= deFloorFloatToInt32(uBounds1.y());
    667 	const int	minJ0				= deFloorFloatToInt32(vBounds0.x());
    668 	const int	maxJ0				= deFloorFloatToInt32(vBounds0.y());
    669 	const int	minJ1				= deFloorFloatToInt32(vBounds1.x());
    670 	const int	maxJ1				= deFloorFloatToInt32(vBounds1.y());
    671 
    672 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
    673 	{
    674 		for (int i0 = minI0; i0 <= maxI0; i0++)
    675 		{
    676 			const float	depth0	= lookupDepth(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ);
    677 
    678 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
    679 			{
    680 				for (int i1 = minI1; i1 <= maxI1; i1++)
    681 				{
    682 					const float	depth1	= lookupDepth(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ);
    683 
    684 					if (isLinearCompareValid(sampler.compare, prec, Vec2(depth0, depth1), fBounds, cmpReference, result, isFixedPointDepth))
    685 						return true;
    686 				}
    687 			}
    688 		}
    689 	}
    690 
    691 	return false;
    692 }
    693 
    694 static bool isLinearMipmapLinearCompareResultValid (const ConstPixelBufferAccess&	level0,
    695 													const ConstPixelBufferAccess&	level1,
    696 													const Sampler&					sampler,
    697 													const TexComparePrecision&		prec,
    698 													const Vec2&						coord,
    699 													const int						coordZ,
    700 													const Vec2&						fBounds,
    701 													const float						cmpReference,
    702 													const float						result)
    703 {
    704 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level0.getFormat());
    705 
    706 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
    707 	//						   Right now this allows pairing any two valid bilinear quads.
    708 
    709 	const int	w0					= level0.getWidth();
    710 	const int	w1					= level1.getWidth();
    711 	const int	h0					= level0.getHeight();
    712 	const int	h1					= level1.getHeight();
    713 
    714 	const Vec2	uBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    715 	const Vec2	uBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    716 	const Vec2	vBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    717 	const Vec2	vBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    718 
    719 	// Integer coordinates - without wrap mode
    720 	const int	minI0				= deFloorFloatToInt32(uBounds0.x()-0.5f);
    721 	const int	maxI0				= deFloorFloatToInt32(uBounds0.y()-0.5f);
    722 	const int	minI1				= deFloorFloatToInt32(uBounds1.x()-0.5f);
    723 	const int	maxI1				= deFloorFloatToInt32(uBounds1.y()-0.5f);
    724 	const int	minJ0				= deFloorFloatToInt32(vBounds0.x()-0.5f);
    725 	const int	maxJ0				= deFloorFloatToInt32(vBounds0.y()-0.5f);
    726 	const int	minJ1				= deFloorFloatToInt32(vBounds1.x()-0.5f);
    727 	const int	maxJ1				= deFloorFloatToInt32(vBounds1.y()-0.5f);
    728 
    729 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
    730 	{
    731 		for (int i0 = minI0; i0 <= maxI0; i0++)
    732 		{
    733 			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
    734 			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
    735 			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
    736 			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
    737 			Vec4		depths0;
    738 
    739 			{
    740 				const int	x0		= wrap(sampler.wrapS, i0  , w0);
    741 				const int	x1		= wrap(sampler.wrapS, i0+1, w0);
    742 				const int	y0		= wrap(sampler.wrapT, j0  , h0);
    743 				const int	y1		= wrap(sampler.wrapT, j0+1, h0);
    744 
    745 				depths0[0] = lookupDepth(level0, sampler, x0, y0, coordZ);
    746 				depths0[1] = lookupDepth(level0, sampler, x1, y0, coordZ);
    747 				depths0[2] = lookupDepth(level0, sampler, x0, y1, coordZ);
    748 				depths0[3] = lookupDepth(level0, sampler, x1, y1, coordZ);
    749 			}
    750 
    751 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
    752 			{
    753 				for (int i1 = minI1; i1 <= maxI1; i1++)
    754 				{
    755 					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
    756 					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
    757 					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
    758 					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
    759 					Vec4		depths1;
    760 
    761 					{
    762 						const int	x0		= wrap(sampler.wrapS, i1  , w1);
    763 						const int	x1		= wrap(sampler.wrapS, i1+1, w1);
    764 						const int	y0		= wrap(sampler.wrapT, j1  , h1);
    765 						const int	y1		= wrap(sampler.wrapT, j1+1, h1);
    766 
    767 						depths1[0] = lookupDepth(level1, sampler, x0, y0, coordZ);
    768 						depths1[1] = lookupDepth(level1, sampler, x1, y0, coordZ);
    769 						depths1[2] = lookupDepth(level1, sampler, x0, y1, coordZ);
    770 						depths1[3] = lookupDepth(level1, sampler, x1, y1, coordZ);
    771 					}
    772 
    773 					if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1,
    774 												Vec2(minA0, maxA0), Vec2(minB0, maxB0),
    775 												Vec2(minA1, maxA1), Vec2(minB1, maxB1),
    776 												fBounds, cmpReference, result, isFixedPointDepth))
    777 						return true;
    778 				}
    779 			}
    780 		}
    781 	}
    782 
    783 	return false;
    784 }
    785 
    786 static bool isMipmapLinearCompareResultValid (const ConstPixelBufferAccess&		level0,
    787 											  const ConstPixelBufferAccess&		level1,
    788 											  const Sampler&					sampler,
    789 											  const Sampler::FilterMode			levelFilter,
    790 											  const TexComparePrecision&		prec,
    791 											  const Vec2&						coord,
    792 											  const int							coordZ,
    793 											  const Vec2&						fBounds,
    794 											  const float						cmpReference,
    795 											  const float						result)
    796 {
    797 	if (levelFilter == Sampler::LINEAR)
    798 		return isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result);
    799 	else
    800 		return isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result);
    801 }
    802 
    803 bool isTexCompareResultValid (const Texture2DView&			texture,
    804 							  const Sampler&				sampler,
    805 							  const TexComparePrecision&	prec,
    806 							  const Vec2&					coord,
    807 							  const Vec2&					lodBounds,
    808 							  const float					cmpReference,
    809 							  const float					result)
    810 {
    811 	const float		minLod			= lodBounds.x();
    812 	const float		maxLod			= lodBounds.y();
    813 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
    814 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
    815 
    816 	DE_ASSERT(isSamplerSupported(sampler));
    817 
    818 	if (canBeMagnified)
    819 	{
    820 		if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference, result))
    821 			return true;
    822 	}
    823 
    824 	if (canBeMinified)
    825 	{
    826 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
    827 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
    828 		const int	minTexLevel		= 0;
    829 		const int	maxTexLevel		= texture.getNumLevels()-1;
    830 
    831 		DE_ASSERT(minTexLevel < maxTexLevel);
    832 
    833 		if (isLinearMipmap)
    834 		{
    835 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
    836 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
    837 
    838 			DE_ASSERT(minLevel <= maxLevel);
    839 
    840 			for (int level = minLevel; level <= maxLevel; level++)
    841 			{
    842 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
    843 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
    844 
    845 				if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), cmpReference, result))
    846 					return true;
    847 			}
    848 		}
    849 		else if (isNearestMipmap)
    850 		{
    851 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
    852 			//		 decision to allow floor(lod + 0.5) as well.
    853 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
    854 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
    855 
    856 			DE_ASSERT(minLevel <= maxLevel);
    857 
    858 			for (int level = minLevel; level <= maxLevel; level++)
    859 			{
    860 				if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, cmpReference, result))
    861 					return true;
    862 			}
    863 		}
    864 		else
    865 		{
    866 			if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference, result))
    867 				return true;
    868 		}
    869 	}
    870 
    871 	return false;
    872 }
    873 
    874 static bool isSeamplessLinearMipmapLinearCompareResultValid (const TextureCubeView&			texture,
    875 															 const int						baseLevelNdx,
    876 															 const Sampler&					sampler,
    877 															 const TexComparePrecision&		prec,
    878 															 const CubeFaceFloatCoords&		coords,
    879 															 const Vec2&					fBounds,
    880 															 const float					cmpReference,
    881 															 const float					result)
    882 {
    883 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getLevelFace(baseLevelNdx, CUBEFACE_NEGATIVE_X).getFormat());
    884 	const int	size0				= texture.getLevelFace(baseLevelNdx,	coords.face).getWidth();
    885 	const int	size1				= texture.getLevelFace(baseLevelNdx+1,	coords.face).getWidth();
    886 
    887 	const Vec2	uBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
    888 	const Vec2	uBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
    889 	const Vec2	vBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
    890 	const Vec2	vBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
    891 
    892 	// Integer coordinates - without wrap mode
    893 	const int	minI0				= deFloorFloatToInt32(uBounds0.x()-0.5f);
    894 	const int	maxI0				= deFloorFloatToInt32(uBounds0.y()-0.5f);
    895 	const int	minI1				= deFloorFloatToInt32(uBounds1.x()-0.5f);
    896 	const int	maxI1				= deFloorFloatToInt32(uBounds1.y()-0.5f);
    897 	const int	minJ0				= deFloorFloatToInt32(vBounds0.x()-0.5f);
    898 	const int	maxJ0				= deFloorFloatToInt32(vBounds0.y()-0.5f);
    899 	const int	minJ1				= deFloorFloatToInt32(vBounds1.x()-0.5f);
    900 	const int	maxJ1				= deFloorFloatToInt32(vBounds1.y()-0.5f);
    901 
    902 	tcu::ConstPixelBufferAccess faces0[CUBEFACE_LAST];
    903 	tcu::ConstPixelBufferAccess faces1[CUBEFACE_LAST];
    904 
    905 	for (int face = 0; face < CUBEFACE_LAST; face++)
    906 	{
    907 		faces0[face] = texture.getLevelFace(baseLevelNdx,	CubeFace(face));
    908 		faces1[face] = texture.getLevelFace(baseLevelNdx+1,	CubeFace(face));
    909 	}
    910 
    911 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
    912 	{
    913 		for (int i0 = minI0; i0 <= maxI0; i0++)
    914 		{
    915 			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
    916 			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
    917 			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
    918 			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
    919 			Vec4		depths0;
    920 
    921 			{
    922 				const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+0)), size0);
    923 				const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+0)), size0);
    924 				const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+1)), size0);
    925 				const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+1)), size0);
    926 
    927 				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
    928 				// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
    929 				if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
    930 					return true;
    931 
    932 				depths0[0] = lookupDepthNoBorder(faces0[c00.face], sampler, c00.s, c00.t);
    933 				depths0[1] = lookupDepthNoBorder(faces0[c10.face], sampler, c10.s, c10.t);
    934 				depths0[2] = lookupDepthNoBorder(faces0[c01.face], sampler, c01.s, c01.t);
    935 				depths0[3] = lookupDepthNoBorder(faces0[c11.face], sampler, c11.s, c11.t);
    936 			}
    937 
    938 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
    939 			{
    940 				for (int i1 = minI1; i1 <= maxI1; i1++)
    941 				{
    942 					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
    943 					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
    944 					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
    945 					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
    946 					Vec4		depths1;
    947 
    948 					{
    949 						const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+0)), size1);
    950 						const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+0)), size1);
    951 						const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+1)), size1);
    952 						const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+1)), size1);
    953 
    954 						if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
    955 							return true;
    956 
    957 						depths1[0] = lookupDepthNoBorder(faces1[c00.face], sampler, c00.s, c00.t);
    958 						depths1[1] = lookupDepthNoBorder(faces1[c10.face], sampler, c10.s, c10.t);
    959 						depths1[2] = lookupDepthNoBorder(faces1[c01.face], sampler, c01.s, c01.t);
    960 						depths1[3] = lookupDepthNoBorder(faces1[c11.face], sampler, c11.s, c11.t);
    961 					}
    962 
    963 
    964 					if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1,
    965 												Vec2(minA0, maxA0), Vec2(minB0, maxB0),
    966 												Vec2(minA1, maxA1), Vec2(minB1, maxB1),
    967 												fBounds, cmpReference, result, isFixedPointDepth))
    968 						return true;
    969 				}
    970 			}
    971 		}
    972 	}
    973 
    974 	return false;
    975 }
    976 
    977 static bool isCubeMipmapLinearCompareResultValid (const TextureCubeView&		texture,
    978 												  const int						baseLevelNdx,
    979 												  const Sampler&				sampler,
    980 												  const Sampler::FilterMode		levelFilter,
    981 												  const TexComparePrecision&	prec,
    982 												  const CubeFaceFloatCoords&	coords,
    983 												  const Vec2&					fBounds,
    984 												  const float					cmpReference,
    985 												  const float					result)
    986 {
    987 	if (levelFilter == Sampler::LINEAR)
    988 	{
    989 		if (sampler.seamlessCubeMap)
    990 			return isSeamplessLinearMipmapLinearCompareResultValid(texture, baseLevelNdx, sampler, prec, coords, fBounds, cmpReference, result);
    991 		else
    992 			return isLinearMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx,	coords.face),
    993 														  texture.getLevelFace(baseLevelNdx+1,	coords.face),
    994 														  sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result);
    995 	}
    996 	else
    997 		return isNearestMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx,	coords.face),
    998 													   texture.getLevelFace(baseLevelNdx+1,	coords.face),
    999 													   sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result);
   1000 }
   1001 
   1002 static bool isSeamlessLinearCompareResultValid (const TextureCubeView&		texture,
   1003 												const int					levelNdx,
   1004 												const Sampler&				sampler,
   1005 												const TexComparePrecision&	prec,
   1006 												const CubeFaceFloatCoords&	coords,
   1007 												const float					cmpReference,
   1008 												const float					result)
   1009 {
   1010 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getLevelFace(levelNdx, CUBEFACE_NEGATIVE_X).getFormat());
   1011 	const int	size				= texture.getLevelFace(levelNdx, coords.face).getWidth();
   1012 
   1013 	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1014 	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1015 
   1016 	// Integer coordinate bounds for (x0,y0) - without wrap mode
   1017 	const int	minI				= deFloorFloatToInt32(uBounds.x()-0.5f);
   1018 	const int	maxI				= deFloorFloatToInt32(uBounds.y()-0.5f);
   1019 	const int	minJ				= deFloorFloatToInt32(vBounds.x()-0.5f);
   1020 	const int	maxJ				= deFloorFloatToInt32(vBounds.y()-0.5f);
   1021 
   1022 	// Face accesses
   1023 	ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1024 	for (int face = 0; face < CUBEFACE_LAST; face++)
   1025 		faces[face] = texture.getLevelFace(levelNdx, CubeFace(face));
   1026 
   1027 	for (int j = minJ; j <= maxJ; j++)
   1028 	{
   1029 		for (int i = minI; i <= maxI; i++)
   1030 		{
   1031 			const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+0)), size);
   1032 			const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+0)), size);
   1033 			const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+1)), size);
   1034 			const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+1)), size);
   1035 
   1036 			// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   1037 			// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
   1038 			if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1039 				return true;
   1040 
   1041 			// Bounds for filtering factors
   1042 			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
   1043 			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
   1044 			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
   1045 			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
   1046 
   1047 			Vec4 depths;
   1048 			depths[0] = lookupDepthNoBorder(faces[c00.face], sampler, c00.s, c00.t);
   1049 			depths[1] = lookupDepthNoBorder(faces[c10.face], sampler, c10.s, c10.t);
   1050 			depths[2] = lookupDepthNoBorder(faces[c01.face], sampler, c01.s, c01.t);
   1051 			depths[3] = lookupDepthNoBorder(faces[c11.face], sampler, c11.s, c11.t);
   1052 
   1053 			if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth))
   1054 				return true;
   1055 		}
   1056 	}
   1057 
   1058 	return false;
   1059 }
   1060 
   1061 static bool isCubeLevelCompareResultValid (const TextureCubeView&			texture,
   1062 										   const int						levelNdx,
   1063 										   const Sampler&					sampler,
   1064 										   const Sampler::FilterMode		filterMode,
   1065 										   const TexComparePrecision&		prec,
   1066 										   const CubeFaceFloatCoords&		coords,
   1067 										   const float						cmpReference,
   1068 										   const float						result)
   1069 {
   1070 	if (filterMode == Sampler::LINEAR)
   1071 	{
   1072 		if (sampler.seamlessCubeMap)
   1073 			return isSeamlessLinearCompareResultValid(texture, levelNdx, sampler, prec, coords, cmpReference, result);
   1074 		else
   1075 			return isLinearCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result);
   1076 	}
   1077 	else
   1078 		return isNearestCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result);
   1079 }
   1080 
   1081 bool isTexCompareResultValid (const TextureCubeView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result)
   1082 {
   1083 	int			numPossibleFaces				= 0;
   1084 	CubeFace	possibleFaces[CUBEFACE_LAST];
   1085 
   1086 	DE_ASSERT(isSamplerSupported(sampler));
   1087 
   1088 	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   1089 
   1090 	if (numPossibleFaces == 0)
   1091 		return true; // Result is undefined.
   1092 
   1093 	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   1094 	{
   1095 		const CubeFaceFloatCoords	faceCoords		(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
   1096 		const float					minLod			= lodBounds.x();
   1097 		const float					maxLod			= lodBounds.y();
   1098 		const bool					canBeMagnified	= minLod <= sampler.lodThreshold;
   1099 		const bool					canBeMinified	= maxLod > sampler.lodThreshold;
   1100 
   1101 		if (canBeMagnified)
   1102 		{
   1103 			if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, cmpReference, result))
   1104 				return true;
   1105 		}
   1106 
   1107 		if (canBeMinified)
   1108 		{
   1109 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1110 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1111 			const int	minTexLevel		= 0;
   1112 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1113 
   1114 			DE_ASSERT(minTexLevel < maxTexLevel);
   1115 
   1116 			if (isLinearMipmap)
   1117 			{
   1118 				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1119 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1120 
   1121 				DE_ASSERT(minLevel <= maxLevel);
   1122 
   1123 				for (int level = minLevel; level <= maxLevel; level++)
   1124 				{
   1125 					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1126 					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1127 
   1128 					if (isCubeMipmapLinearCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), cmpReference, result))
   1129 						return true;
   1130 				}
   1131 			}
   1132 			else if (isNearestMipmap)
   1133 			{
   1134 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1135 				//		 decision to allow floor(lod + 0.5) as well.
   1136 				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1137 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1138 
   1139 				DE_ASSERT(minLevel <= maxLevel);
   1140 
   1141 				for (int level = minLevel; level <= maxLevel; level++)
   1142 				{
   1143 					if (isCubeLevelCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, cmpReference, result))
   1144 						return true;
   1145 				}
   1146 			}
   1147 			else
   1148 			{
   1149 				if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords, cmpReference, result))
   1150 					return true;
   1151 			}
   1152 		}
   1153 	}
   1154 
   1155 	return false;
   1156 }
   1157 
   1158 bool isTexCompareResultValid (const Texture2DArrayView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result)
   1159 {
   1160 	const float		depthErr	= computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
   1161 	const float		minZ		= coord.z()-depthErr;
   1162 	const float		maxZ		= coord.z()+depthErr;
   1163 	const int		minLayer	= de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1);
   1164 	const int		maxLayer	= de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1);
   1165 
   1166 	DE_ASSERT(isSamplerSupported(sampler));
   1167 
   1168 	for (int layer = minLayer; layer <= maxLayer; layer++)
   1169 	{
   1170 		const float		minLod			= lodBounds.x();
   1171 		const float		maxLod			= lodBounds.y();
   1172 		const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1173 		const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1174 
   1175 		if (canBeMagnified)
   1176 		{
   1177 			if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord.swizzle(0,1), layer, cmpReference, result))
   1178 				return true;
   1179 		}
   1180 
   1181 		if (canBeMinified)
   1182 		{
   1183 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1184 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1185 			const int	minTexLevel		= 0;
   1186 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1187 
   1188 			DE_ASSERT(minTexLevel < maxTexLevel);
   1189 
   1190 			if (isLinearMipmap)
   1191 			{
   1192 				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1193 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1194 
   1195 				DE_ASSERT(minLevel <= maxLevel);
   1196 
   1197 				for (int level = minLevel; level <= maxLevel; level++)
   1198 				{
   1199 					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1200 					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1201 
   1202 					if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, Vec2(minF, maxF), cmpReference, result))
   1203 						return true;
   1204 				}
   1205 			}
   1206 			else if (isNearestMipmap)
   1207 			{
   1208 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1209 				//		 decision to allow floor(lod + 0.5) as well.
   1210 				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1211 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1212 
   1213 				DE_ASSERT(minLevel <= maxLevel);
   1214 
   1215 				for (int level = minLevel; level <= maxLevel; level++)
   1216 				{
   1217 					if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, cmpReference, result))
   1218 						return true;
   1219 				}
   1220 			}
   1221 			else
   1222 			{
   1223 				if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord.swizzle(0,1), layer, cmpReference, result))
   1224 					return true;
   1225 			}
   1226 		}
   1227 	}
   1228 
   1229 	return false;
   1230 }
   1231 
   1232 static bool isGatherOffsetsCompareResultValid (const ConstPixelBufferAccess&	texture,
   1233 											   const Sampler&					sampler,
   1234 											   const TexComparePrecision&		prec,
   1235 											   const Vec2&						coord,
   1236 											   int								coordZ,
   1237 											   const IVec2						(&offsets)[4],
   1238 											   float							cmpReference,
   1239 											   const Vec4&						result)
   1240 {
   1241 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getFormat());
   1242 	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getWidth(),		coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1243 	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1244 
   1245 	// Integer coordinate bounds for (x0, y0) - without wrap mode
   1246 	const int	minI				= deFloorFloatToInt32(uBounds.x()-0.5f);
   1247 	const int	maxI				= deFloorFloatToInt32(uBounds.y()-0.5f);
   1248 	const int	minJ				= deFloorFloatToInt32(vBounds.x()-0.5f);
   1249 	const int	maxJ				= deFloorFloatToInt32(vBounds.y()-0.5f);
   1250 
   1251 	const int	w					= texture.getWidth();
   1252 	const int	h					= texture.getHeight();
   1253 
   1254 	for (int j = minJ; j <= maxJ; j++)
   1255 	{
   1256 		for (int i = minI; i <= maxI; i++)
   1257 		{
   1258 			bool isCurrentPixelValid = true;
   1259 
   1260 			for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++)
   1261 			{
   1262 				// offNdx-th coordinate offset and then wrapped.
   1263 				const int			x		= wrap(sampler.wrapS, i+offsets[offNdx].x(), w);
   1264 				const int			y		= wrap(sampler.wrapT, j+offsets[offNdx].y(), h);
   1265 				const float			depth	= lookupDepth(texture, sampler, x, y, coordZ);
   1266 				const CmpResultSet	resSet	= execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
   1267 
   1268 				if (!isResultInSet(resSet, result[offNdx], prec.resultBits))
   1269 					isCurrentPixelValid = false;
   1270 			}
   1271 
   1272 			if (isCurrentPixelValid)
   1273 				return true;
   1274 		}
   1275 	}
   1276 
   1277 	return false;
   1278 }
   1279 
   1280 bool isGatherOffsetsCompareResultValid (const Texture2DView&		texture,
   1281 										const Sampler&				sampler,
   1282 										const TexComparePrecision&	prec,
   1283 										const Vec2&					coord,
   1284 										const IVec2					(&offsets)[4],
   1285 										float						cmpReference,
   1286 										const Vec4&					result)
   1287 {
   1288 	DE_ASSERT(isSamplerSupported(sampler));
   1289 
   1290 	return isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord, 0, offsets, cmpReference, result);
   1291 }
   1292 
   1293 bool isGatherOffsetsCompareResultValid (const Texture2DArrayView&	texture,
   1294 										const Sampler&				sampler,
   1295 										const TexComparePrecision&	prec,
   1296 										const Vec3&					coord,
   1297 										const IVec2					(&offsets)[4],
   1298 										float						cmpReference,
   1299 										const Vec4&					result)
   1300 {
   1301 	const float		depthErr	= computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
   1302 	const float		minZ		= coord.z()-depthErr;
   1303 	const float		maxZ		= coord.z()+depthErr;
   1304 	const int		minLayer	= de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1);
   1305 	const int		maxLayer	= de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1);
   1306 
   1307 	DE_ASSERT(isSamplerSupported(sampler));
   1308 
   1309 	for (int layer = minLayer; layer <= maxLayer; layer++)
   1310 	{
   1311 		if (isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0,1), layer, offsets, cmpReference, result))
   1312 			return true;
   1313 	}
   1314 	return false;
   1315 }
   1316 
   1317 static bool isGatherCompareResultValid (const TextureCubeView&		texture,
   1318 										const Sampler&				sampler,
   1319 										const TexComparePrecision&	prec,
   1320 										const CubeFaceFloatCoords&	coords,
   1321 										float						cmpReference,
   1322 										const Vec4&					result)
   1323 {
   1324 	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getLevelFace(0, coords.face).getFormat());
   1325 	const int	size				= texture.getLevelFace(0, coords.face).getWidth();
   1326 	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1327 	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1328 
   1329 	// Integer coordinate bounds for (x0,y0) - without wrap mode
   1330 	const int	minI				= deFloorFloatToInt32(uBounds.x()-0.5f);
   1331 	const int	maxI				= deFloorFloatToInt32(uBounds.y()-0.5f);
   1332 	const int	minJ				= deFloorFloatToInt32(vBounds.x()-0.5f);
   1333 	const int	maxJ				= deFloorFloatToInt32(vBounds.y()-0.5f);
   1334 
   1335 	// Face accesses
   1336 	ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1337 	for (int face = 0; face < CUBEFACE_LAST; face++)
   1338 		faces[face] = texture.getLevelFace(0, CubeFace(face));
   1339 
   1340 	for (int j = minJ; j <= maxJ; j++)
   1341 	{
   1342 		for (int i = minI; i <= maxI; i++)
   1343 		{
   1344 			static const IVec2 offsets[4] =
   1345 			{
   1346 				IVec2(0, 1),
   1347 				IVec2(1, 1),
   1348 				IVec2(1, 0),
   1349 				IVec2(0, 0)
   1350 			};
   1351 
   1352 			bool isCurrentPixelValid = true;
   1353 
   1354 			for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++)
   1355 			{
   1356 				const CubeFaceIntCoords c = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, i+offsets[offNdx].x(), j+offsets[offNdx].y()), size);
   1357 				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   1358 				// \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color.
   1359 				//							 See also isSeamlessLinearCompareResultValid and similar.
   1360 				if (c.face == CUBEFACE_LAST)
   1361 					return true;
   1362 
   1363 				const float			depth	= lookupDepthNoBorder(faces[c.face], sampler, c.s, c.t);
   1364 				const CmpResultSet	resSet	= execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
   1365 
   1366 				if (!isResultInSet(resSet, result[offNdx], prec.resultBits))
   1367 					isCurrentPixelValid = false;
   1368 			}
   1369 
   1370 			if (isCurrentPixelValid)
   1371 				return true;
   1372 		}
   1373 	}
   1374 
   1375 	return false;
   1376 }
   1377 
   1378 bool isGatherCompareResultValid (const TextureCubeView&			texture,
   1379 								 const Sampler&					sampler,
   1380 								 const TexComparePrecision&		prec,
   1381 								 const Vec3&					coord,
   1382 								 float							cmpReference,
   1383 								 const Vec4&					result)
   1384 {
   1385 	int			numPossibleFaces				= 0;
   1386 	CubeFace	possibleFaces[CUBEFACE_LAST];
   1387 
   1388 	DE_ASSERT(isSamplerSupported(sampler));
   1389 
   1390 	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   1391 
   1392 	if (numPossibleFaces == 0)
   1393 		return true; // Result is undefined.
   1394 
   1395 	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   1396 	{
   1397 		const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
   1398 
   1399 		if (isGatherCompareResultValid(texture, sampler, prec, faceCoords, cmpReference, result))
   1400 			return true;
   1401 	}
   1402 
   1403 	return false;
   1404 }
   1405 
   1406 } // tcu
   1407