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