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 lookup simulator that is capable of verifying generic
     22  *		  lookup results based on accuracy parameters.
     23  *//*--------------------------------------------------------------------*/
     24 
     25 #include "tcuTexLookupVerifier.hpp"
     26 #include "tcuTexVerifierUtil.hpp"
     27 #include "tcuVectorUtil.hpp"
     28 #include "tcuTextureUtil.hpp"
     29 #include "deMath.h"
     30 
     31 namespace tcu
     32 {
     33 
     34 using namespace TexVerifierUtil;
     35 
     36 // Generic utilities
     37 
     38 #if defined(DE_DEBUG)
     39 static bool isSamplerSupported (const Sampler& sampler)
     40 {
     41 	return sampler.compare == Sampler::COMPAREMODE_NONE &&
     42 		   isWrapModeSupported(sampler.wrapS)			&&
     43 		   isWrapModeSupported(sampler.wrapT)			&&
     44 		   isWrapModeSupported(sampler.wrapR);
     45 }
     46 #endif // DE_DEBUG
     47 
     48 // Color read & compare utilities
     49 
     50 static inline bool coordsInBounds (const ConstPixelBufferAccess& access, int x, int y, int z)
     51 {
     52 	return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) && de::inBounds(z, 0, access.getDepth());
     53 }
     54 
     55 template<typename ScalarType>
     56 inline Vector<ScalarType, 4> lookup (const ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k)
     57 {
     58 	if (coordsInBounds(access, i, j, k))
     59 		return access.getPixelT<ScalarType>(i, j, k);
     60 	else
     61 		return sampleTextureBorder<ScalarType>(access.getFormat(), sampler);
     62 }
     63 
     64 template<>
     65 inline Vector<float, 4> lookup (const ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k)
     66 {
     67 	// Specialization for float lookups: sRGB conversion is performed as specified in format.
     68 	if (coordsInBounds(access, i, j, k))
     69 	{
     70 		const Vec4 p = access.getPixel(i, j, k);
     71 		return isSRGB(access.getFormat()) ? sRGBToLinear(p) : p;
     72 	}
     73 	else
     74 		return sampleTextureBorder<float>(access.getFormat(), sampler);
     75 }
     76 
     77 static inline bool isColorValid (const LookupPrecision& prec, const Vec4& ref, const Vec4& result)
     78 {
     79 	const Vec4 diff = abs(ref - result);
     80 	return boolAll(logicalOr(lessThanEqual(diff, prec.colorThreshold), logicalNot(prec.colorMask)));
     81 }
     82 
     83 static inline bool isColorValid (const IntLookupPrecision& prec, const IVec4& ref, const IVec4& result)
     84 {
     85 	return boolAll(logicalOr(lessThanEqual(absDiff(ref, result).asUint(), prec.colorThreshold), logicalNot(prec.colorMask)));
     86 }
     87 
     88 static inline bool isColorValid (const IntLookupPrecision& prec, const UVec4& ref, const UVec4& result)
     89 {
     90 	return boolAll(logicalOr(lessThanEqual(absDiff(ref, result), prec.colorThreshold), logicalNot(prec.colorMask)));
     91 }
     92 
     93 struct ColorQuad
     94 {
     95 	Vec4	p00;		//!< (0, 0)
     96 	Vec4	p01;		//!< (1, 0)
     97 	Vec4	p10;		//!< (0, 1)
     98 	Vec4	p11;		//!< (1, 1)
     99 };
    100 
    101 static void lookupQuad (ColorQuad& dst, const ConstPixelBufferAccess& level, const Sampler& sampler, int x0, int x1, int y0, int y1, int z)
    102 {
    103 	dst.p00	= lookup<float>(level, sampler, x0, y0, z);
    104 	dst.p10	= lookup<float>(level, sampler, x1, y0, z);
    105 	dst.p01	= lookup<float>(level, sampler, x0, y1, z);
    106 	dst.p11	= lookup<float>(level, sampler, x1, y1, z);
    107 }
    108 
    109 struct ColorLine
    110 {
    111 	Vec4	p0;		//!< 0
    112 	Vec4	p1;		//!< 1
    113 };
    114 
    115 static void lookupLine (ColorLine& dst, const ConstPixelBufferAccess& level, const Sampler& sampler, int x0, int x1, int y)
    116 {
    117 	dst.p0 = lookup<float>(level, sampler, x0, y, 0);
    118 	dst.p1 = lookup<float>(level, sampler, x1, y, 0);
    119 }
    120 
    121 template<typename T, int Size>
    122 static T minComp (const Vector<T, Size>& vec)
    123 {
    124 	T minVal = vec[0];
    125 	for (int ndx = 1; ndx < Size; ndx++)
    126 		minVal = de::min(minVal, vec[ndx]);
    127 	return minVal;
    128 }
    129 
    130 template<typename T, int Size>
    131 static T maxComp (const Vector<T, Size>& vec)
    132 {
    133 	T maxVal = vec[0];
    134 	for (int ndx = 1; ndx < Size; ndx++)
    135 		maxVal = de::max(maxVal, vec[ndx]);
    136 	return maxVal;
    137 }
    138 
    139 static float computeBilinearSearchStepFromFloatLine (const LookupPrecision&	prec,
    140 													 const ColorLine&		line)
    141 {
    142 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    143 
    144 	const int		maxSteps	= 1<<16;
    145 	const Vec4		d			= abs(line.p1 - line.p0);
    146 	const Vec4		stepCount	= d / prec.colorThreshold;
    147 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    148 	const float		step		= de::max(minComp(minStep), 1.0f / float(maxSteps));
    149 
    150 	return step;
    151 }
    152 
    153 static float computeBilinearSearchStepFromFloatQuad (const LookupPrecision&	prec,
    154 													 const ColorQuad&		quad)
    155 {
    156 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    157 
    158 	const int		maxSteps	= 1<<16;
    159 	const Vec4		d0			= abs(quad.p10 - quad.p00);
    160 	const Vec4		d1			= abs(quad.p01 - quad.p00);
    161 	const Vec4		d2			= abs(quad.p11 - quad.p10);
    162 	const Vec4		d3			= abs(quad.p11 - quad.p01);
    163 	const Vec4		maxD		= max(d0, max(d1, max(d2, d3)));
    164 	const Vec4		stepCount	= maxD / prec.colorThreshold;
    165 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    166 	const float		step		= de::max(minComp(minStep), 1.0f / float(maxSteps));
    167 
    168 	return step;
    169 }
    170 
    171 static float computeBilinearSearchStepForUnorm (const LookupPrecision& prec)
    172 {
    173 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    174 
    175 	const Vec4		stepCount	= 1.0f / prec.colorThreshold;
    176 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    177 	const float		step		= minComp(minStep);
    178 
    179 	return step;
    180 }
    181 
    182 static float computeBilinearSearchStepForSnorm (const LookupPrecision& prec)
    183 {
    184 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    185 
    186 	const Vec4		stepCount	= 2.0f / prec.colorThreshold;
    187 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    188 	const float		step		= minComp(minStep);
    189 
    190 	return step;
    191 }
    192 
    193 static inline Vec4 min (const ColorLine& line)
    194 {
    195 	return min(line.p0, line.p1);
    196 }
    197 
    198 static inline Vec4 max (const ColorLine& line)
    199 {
    200 	return max(line.p0, line.p1);
    201 }
    202 
    203 static inline Vec4 min (const ColorQuad& quad)
    204 {
    205 	return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11)));
    206 }
    207 
    208 static inline Vec4 max (const ColorQuad& quad)
    209 {
    210 	return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11)));
    211 }
    212 
    213 static bool isInColorBounds (const LookupPrecision& prec, const ColorQuad& quad, const Vec4& result)
    214 {
    215 	const tcu::Vec4 minVal = min(quad) - prec.colorThreshold;
    216 	const tcu::Vec4 maxVal = max(quad) + prec.colorThreshold;
    217 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    218 }
    219 
    220 static bool isInColorBounds (const LookupPrecision& prec, const ColorQuad& quad0, const ColorQuad& quad1, const Vec4& result)
    221 {
    222 	const tcu::Vec4 minVal = min(min(quad0), min(quad1)) - prec.colorThreshold;
    223 	const tcu::Vec4 maxVal = max(max(quad0), max(quad1)) + prec.colorThreshold;
    224 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    225 }
    226 
    227 static bool isInColorBounds (const LookupPrecision& prec, const ColorLine& line0, const ColorLine& line1, const Vec4& result)
    228 {
    229 	const tcu::Vec4 minVal = min(min(line0), min(line1)) - prec.colorThreshold;
    230 	const tcu::Vec4 maxVal = max(max(line0), max(line1)) + prec.colorThreshold;
    231 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    232 }
    233 
    234 static bool isInColorBounds (const LookupPrecision&		prec,
    235 							 const ColorQuad&			quad00,
    236 							 const ColorQuad&			quad01,
    237 							 const ColorQuad&			quad10,
    238 							 const ColorQuad&			quad11,
    239 							 const Vec4&				result)
    240 {
    241 	const tcu::Vec4 minVal = min(min(quad00), min(min(quad01), min(min(quad10), min(quad11)))) - prec.colorThreshold;
    242 	const tcu::Vec4 maxVal = max(max(quad00), max(max(quad01), max(max(quad10), max(quad11)))) + prec.colorThreshold;
    243 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    244 }
    245 
    246 // Range search utilities
    247 
    248 static bool isLinearRangeValid (const LookupPrecision&	prec,
    249 								const Vec4&				c0,
    250 								const Vec4&				c1,
    251 								const Vec2&				fBounds,
    252 								const Vec4&				result)
    253 {
    254 	// This is basically line segment - AABB test. Valid interpolation line is checked
    255 	// against result AABB constructed by applying threshold.
    256 
    257 	const Vec4		i0				= c0*(1.0f - fBounds[0]) + c1*fBounds[0];
    258 	const Vec4		i1				= c0*(1.0f - fBounds[1]) + c1*fBounds[1];
    259 	const Vec4		rMin			= result - prec.colorThreshold;
    260 	const Vec4		rMax			= result + prec.colorThreshold;
    261 	bool			allIntersect	= true;
    262 
    263 	// Algorithm: For each component check whether segment endpoints are inside, or intersect with slab.
    264 	// If all intersect or are inside, line segment intersects the whole 4D AABB.
    265 	for (int compNdx = 0; compNdx < 4; compNdx++)
    266 	{
    267 		if (!prec.colorMask[compNdx])
    268 			continue;
    269 
    270 		// Signs for both bounds: false = left, true = right.
    271 		const bool	sMin0	= i0[compNdx] >= rMin[compNdx];
    272 		const bool	sMin1	= i1[compNdx] >= rMin[compNdx];
    273 		const bool	sMax0	= i0[compNdx] > rMax[compNdx];
    274 		const bool	sMax1	= i1[compNdx] > rMax[compNdx];
    275 
    276 		// If all signs are equal, line segment is outside bounds.
    277 		if (sMin0 == sMin1 && sMin1 == sMax0 && sMax0 == sMax1)
    278 		{
    279 			allIntersect = false;
    280 			break;
    281 		}
    282 	}
    283 
    284 	return allIntersect;
    285 }
    286 
    287 static bool isBilinearRangeValid (const LookupPrecision&	prec,
    288 								  const ColorQuad&			quad,
    289 								  const Vec2&				xBounds,
    290 								  const Vec2&				yBounds,
    291 								  const float				searchStep,
    292 								  const Vec4&				result)
    293 {
    294 	DE_ASSERT(xBounds.x() <= xBounds.y());
    295 	DE_ASSERT(yBounds.x() <= yBounds.y());
    296 	DE_ASSERT(xBounds.x() + searchStep > xBounds.x()); // step is not effectively 0
    297 	DE_ASSERT(xBounds.y() + searchStep > xBounds.y());
    298 
    299 	if (!isInColorBounds(prec, quad, result))
    300 		return false;
    301 
    302 	for (float x = xBounds.x(); x < xBounds.y()+searchStep; x += searchStep)
    303 	{
    304 		const float		a	= de::min(x, xBounds.y());
    305 		const Vec4		c0	= quad.p00*(1.0f - a) + quad.p10*a;
    306 		const Vec4		c1	= quad.p01*(1.0f - a) + quad.p11*a;
    307 
    308 		if (isLinearRangeValid(prec, c0, c1, yBounds, result))
    309 			return true;
    310 	}
    311 
    312 	return false;
    313 }
    314 
    315 static bool isTrilinearRangeValid (const LookupPrecision&	prec,
    316 								   const ColorQuad&			quad0,
    317 								   const ColorQuad&			quad1,
    318 								   const Vec2&				xBounds,
    319 								   const Vec2&				yBounds,
    320 								   const Vec2&				zBounds,
    321 								   const float				searchStep,
    322 								   const Vec4&				result)
    323 {
    324 	DE_ASSERT(xBounds.x() <= xBounds.y());
    325 	DE_ASSERT(yBounds.x() <= yBounds.y());
    326 	DE_ASSERT(zBounds.x() <= zBounds.y());
    327 	DE_ASSERT(xBounds.x() + searchStep > xBounds.x()); // step is not effectively 0
    328 	DE_ASSERT(xBounds.y() + searchStep > xBounds.y());
    329 	DE_ASSERT(yBounds.x() + searchStep > yBounds.x());
    330 	DE_ASSERT(yBounds.y() + searchStep > yBounds.y());
    331 
    332 	if (!isInColorBounds(prec, quad0, quad1, result))
    333 		return false;
    334 
    335 	for (float x = xBounds.x(); x < xBounds.y()+searchStep; x += searchStep)
    336 	{
    337 		for (float y = yBounds.x(); y < yBounds.y()+searchStep; y += searchStep)
    338 		{
    339 			const float		a	= de::min(x, xBounds.y());
    340 			const float		b	= de::min(y, yBounds.y());
    341 			const Vec4		c0	= quad0.p00*(1.0f-a)*(1.0f-b) + quad0.p10*a*(1.0f-b) + quad0.p01*(1.0f-a)*b + quad0.p11*a*b;
    342 			const Vec4		c1	= quad1.p00*(1.0f-a)*(1.0f-b) + quad1.p10*a*(1.0f-b) + quad1.p01*(1.0f-a)*b + quad1.p11*a*b;
    343 
    344 			if (isLinearRangeValid(prec, c0, c1, zBounds, result))
    345 				return true;
    346 		}
    347 	}
    348 
    349 	return false;
    350 }
    351 
    352 static bool isReductionValid (const LookupPrecision&		prec,
    353 							  const Vec4&					c0,
    354 							  const Vec4&					c1,
    355 							  tcu::Sampler::ReductionMode	reductionMode,
    356 							  const Vec4&					result)
    357 {
    358 	DE_ASSERT(reductionMode == tcu::Sampler::MIN || reductionMode == tcu::Sampler::MAX);
    359 
    360 	const Vec4 color = (reductionMode == tcu::Sampler::MIN ? tcu::min(c0, c1) : tcu::max(c0, c1));
    361 
    362 	return isColorValid(prec, color, result);
    363 }
    364 
    365 static bool isReductionValid (const LookupPrecision&		prec,
    366 							  const ColorQuad&				quad,
    367 							  tcu::Sampler::ReductionMode	reductionMode,
    368 							  const Vec4&					result)
    369 {
    370 	DE_ASSERT(reductionMode == tcu::Sampler::MIN || reductionMode == tcu::Sampler::MAX);
    371 
    372 	const Vec4 c0 = (reductionMode == tcu::Sampler::MIN ? tcu::min(quad.p00, quad.p01) : tcu::max(quad.p00, quad.p01));
    373 	const Vec4 c1 = (reductionMode == tcu::Sampler::MIN ? tcu::min(quad.p10, quad.p11) : tcu::max(quad.p10, quad.p11));
    374 
    375 	return isReductionValid(prec, c0, c1, reductionMode, result);
    376 }
    377 
    378 static bool isReductionValid (const LookupPrecision&		prec,
    379 							  const ColorQuad&				quad0,
    380 							  const ColorQuad&				quad1,
    381 							  tcu::Sampler::ReductionMode	reductionMode,
    382 							  const Vec4&					result)
    383 {
    384 	DE_ASSERT(reductionMode == tcu::Sampler::MIN || reductionMode == tcu::Sampler::MAX);
    385 
    386 	const ColorQuad quad =
    387 	{
    388 		reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p00, quad1.p00) : tcu::max(quad0.p00, quad1.p00),			// p00
    389 		reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p01, quad1.p01) : tcu::max(quad0.p01, quad1.p01),			// p01
    390 		reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p10, quad1.p10) : tcu::max(quad0.p10, quad1.p10),			// p10
    391 		reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p11, quad1.p11) : tcu::max(quad0.p11, quad1.p11),			// p11
    392 	};
    393 
    394 	return isReductionValid(prec, quad, reductionMode, result);
    395 }
    396 
    397 static bool is1DTrilinearFilterResultValid (const LookupPrecision&	prec,
    398 											const ColorLine&		line0,
    399 											const ColorLine&		line1,
    400 											const Vec2&				xBounds0,
    401 											const Vec2&				xBounds1,
    402 											const Vec2&				zBounds,
    403 											const float				searchStep,
    404 											const Vec4&				result)
    405 {
    406 	DE_ASSERT(xBounds0.x() <= xBounds0.y());
    407 	DE_ASSERT(xBounds1.x() <= xBounds1.y());
    408 	DE_ASSERT(xBounds0.x() + searchStep > xBounds0.x()); // step is not effectively 0
    409 	DE_ASSERT(xBounds0.y() + searchStep > xBounds0.y());
    410 	DE_ASSERT(xBounds1.x() + searchStep > xBounds1.x());
    411 	DE_ASSERT(xBounds1.y() + searchStep > xBounds1.y());
    412 
    413 	if (!isInColorBounds(prec, line0, line1, result))
    414 		return false;
    415 
    416 	for (float x0 = xBounds0.x(); x0 < xBounds0.y()+searchStep; x0 += searchStep)
    417 	{
    418 		const float		a0	= de::min(x0, xBounds0.y());
    419 		const Vec4		c0	= line0.p0*(1.0f-a0) + line0.p1*a0;
    420 
    421 		for (float x1 = xBounds1.x(); x1 <= xBounds1.y(); x1 += searchStep)
    422 		{
    423 			const float		a1	= de::min(x1, xBounds1.y());
    424 			const Vec4		c1	= line1.p0*(1.0f-a1) + line1.p1*a1;
    425 
    426 			if (isLinearRangeValid(prec, c0, c1, zBounds, result))
    427 				return true;
    428 		}
    429 	}
    430 
    431 	return false;
    432 }
    433 
    434 static bool is2DTrilinearFilterResultValid (const LookupPrecision&	prec,
    435 											const ColorQuad&		quad0,
    436 											const ColorQuad&		quad1,
    437 											const Vec2&				xBounds0,
    438 											const Vec2&				yBounds0,
    439 											const Vec2&				xBounds1,
    440 											const Vec2&				yBounds1,
    441 											const Vec2&				zBounds,
    442 											const float				searchStep,
    443 											const Vec4&				result)
    444 {
    445 	DE_ASSERT(xBounds0.x() <= xBounds0.y());
    446 	DE_ASSERT(yBounds0.x() <= yBounds0.y());
    447 	DE_ASSERT(xBounds1.x() <= xBounds1.y());
    448 	DE_ASSERT(yBounds1.x() <= yBounds1.y());
    449 	DE_ASSERT(xBounds0.x() + searchStep > xBounds0.x()); // step is not effectively 0
    450 	DE_ASSERT(xBounds0.y() + searchStep > xBounds0.y());
    451 	DE_ASSERT(yBounds0.x() + searchStep > yBounds0.x());
    452 	DE_ASSERT(yBounds0.y() + searchStep > yBounds0.y());
    453 	DE_ASSERT(xBounds1.x() + searchStep > xBounds1.x());
    454 	DE_ASSERT(xBounds1.y() + searchStep > xBounds1.y());
    455 	DE_ASSERT(yBounds1.x() + searchStep > yBounds1.x());
    456 	DE_ASSERT(yBounds1.y() + searchStep > yBounds1.y());
    457 
    458 	if (!isInColorBounds(prec, quad0, quad1, result))
    459 		return false;
    460 
    461 	for (float x0 = xBounds0.x(); x0 < xBounds0.y()+searchStep; x0 += searchStep)
    462 	{
    463 		for (float y0 = yBounds0.x(); y0 < yBounds0.y()+searchStep; y0 += searchStep)
    464 		{
    465 			const float		a0	= de::min(x0, xBounds0.y());
    466 			const float		b0	= de::min(y0, yBounds0.y());
    467 			const Vec4		c0	= quad0.p00*(1.0f-a0)*(1.0f-b0) + quad0.p10*a0*(1.0f-b0) + quad0.p01*(1.0f-a0)*b0 + quad0.p11*a0*b0;
    468 
    469 			for (float x1 = xBounds1.x(); x1 <= xBounds1.y(); x1 += searchStep)
    470 			{
    471 				for (float y1 = yBounds1.x(); y1 <= yBounds1.y(); y1 += searchStep)
    472 				{
    473 					const float		a1	= de::min(x1, xBounds1.y());
    474 					const float		b1	= de::min(y1, yBounds1.y());
    475 					const Vec4		c1	= quad1.p00*(1.0f-a1)*(1.0f-b1) + quad1.p10*a1*(1.0f-b1) + quad1.p01*(1.0f-a1)*b1 + quad1.p11*a1*b1;
    476 
    477 					if (isLinearRangeValid(prec, c0, c1, zBounds, result))
    478 						return true;
    479 				}
    480 			}
    481 		}
    482 	}
    483 
    484 	return false;
    485 }
    486 
    487 static bool is3DTrilinearFilterResultValid (const LookupPrecision&	prec,
    488 											const ColorQuad&		quad00,
    489 											const ColorQuad&		quad01,
    490 											const ColorQuad&		quad10,
    491 											const ColorQuad&		quad11,
    492 											const Vec2&				xBounds0,
    493 											const Vec2&				yBounds0,
    494 											const Vec2&				zBounds0,
    495 											const Vec2&				xBounds1,
    496 											const Vec2&				yBounds1,
    497 											const Vec2&				zBounds1,
    498 											const Vec2&				wBounds,
    499 											const float				searchStep,
    500 											const Vec4&				result)
    501 {
    502 	DE_ASSERT(xBounds0.x() <= xBounds0.y());
    503 	DE_ASSERT(yBounds0.x() <= yBounds0.y());
    504 	DE_ASSERT(zBounds0.x() <= zBounds0.y());
    505 	DE_ASSERT(xBounds1.x() <= xBounds1.y());
    506 	DE_ASSERT(yBounds1.x() <= yBounds1.y());
    507 	DE_ASSERT(zBounds1.x() <= zBounds1.y());
    508 	DE_ASSERT(xBounds0.x() + searchStep > xBounds0.x()); // step is not effectively 0
    509 	DE_ASSERT(xBounds0.y() + searchStep > xBounds0.y());
    510 	DE_ASSERT(yBounds0.x() + searchStep > yBounds0.x());
    511 	DE_ASSERT(yBounds0.y() + searchStep > yBounds0.y());
    512 	DE_ASSERT(zBounds0.x() + searchStep > zBounds0.x());
    513 	DE_ASSERT(zBounds0.y() + searchStep > zBounds0.y());
    514 	DE_ASSERT(xBounds1.x() + searchStep > xBounds1.x());
    515 	DE_ASSERT(xBounds1.y() + searchStep > xBounds1.y());
    516 	DE_ASSERT(yBounds1.x() + searchStep > yBounds1.x());
    517 	DE_ASSERT(yBounds1.y() + searchStep > yBounds1.y());
    518 	DE_ASSERT(zBounds1.x() + searchStep > zBounds1.x());
    519 	DE_ASSERT(zBounds1.y() + searchStep > zBounds1.y());
    520 
    521 	if (!isInColorBounds(prec, quad00, quad01, quad10, quad11, result))
    522 		return false;
    523 
    524 	for (float x0 = xBounds0.x(); x0 < xBounds0.y()+searchStep; x0 += searchStep)
    525 	{
    526 		for (float y0 = yBounds0.x(); y0 < yBounds0.y()+searchStep; y0 += searchStep)
    527 		{
    528 			const float		a0	= de::min(x0, xBounds0.y());
    529 			const float		b0	= de::min(y0, yBounds0.y());
    530 			const Vec4		c00	= quad00.p00*(1.0f-a0)*(1.0f-b0) + quad00.p10*a0*(1.0f-b0) + quad00.p01*(1.0f-a0)*b0 + quad00.p11*a0*b0;
    531 			const Vec4		c01	= quad01.p00*(1.0f-a0)*(1.0f-b0) + quad01.p10*a0*(1.0f-b0) + quad01.p01*(1.0f-a0)*b0 + quad01.p11*a0*b0;
    532 
    533 			for (float z0 = zBounds0.x(); z0 < zBounds0.y()+searchStep; z0 += searchStep)
    534 			{
    535 				const float		c0	= de::min(z0, zBounds0.y());
    536 				const Vec4		cz0	= c00*(1.0f-c0) + c01*c0;
    537 
    538 				for (float x1 = xBounds1.x(); x1 < xBounds1.y()+searchStep; x1 += searchStep)
    539 				{
    540 					for (float y1 = yBounds1.x(); y1 < yBounds1.y()+searchStep; y1 += searchStep)
    541 					{
    542 						const float		a1	= de::min(x1, xBounds1.y());
    543 						const float		b1	= de::min(y1, yBounds1.y());
    544 						const Vec4		c10	= quad10.p00*(1.0f-a1)*(1.0f-b1) + quad10.p10*a1*(1.0f-b1) + quad10.p01*(1.0f-a1)*b1 + quad10.p11*a1*b1;
    545 						const Vec4		c11	= quad11.p00*(1.0f-a1)*(1.0f-b1) + quad11.p10*a1*(1.0f-b1) + quad11.p01*(1.0f-a1)*b1 + quad11.p11*a1*b1;
    546 
    547 						for (float z1 = zBounds1.x(); z1 < zBounds1.y()+searchStep; z1 += searchStep)
    548 						{
    549 							const float		c1	= de::min(z1, zBounds1.y());
    550 							const Vec4		cz1	= c10*(1.0f - c1) + c11*c1;
    551 
    552 							if (isLinearRangeValid(prec, cz0, cz1, wBounds, result))
    553 								return true;
    554 						}
    555 					}
    556 				}
    557 			}
    558 		}
    559 	}
    560 
    561 	return false;
    562 }
    563 
    564 template<typename PrecType, typename ScalarType>
    565 static bool isNearestSampleResultValid (const ConstPixelBufferAccess&		level,
    566 										const Sampler&						sampler,
    567 										const PrecType&						prec,
    568 										const float							coordX,
    569 										const int							coordY,
    570 										const Vector<ScalarType, 4>&		result)
    571 {
    572 	DE_ASSERT(level.getDepth() == 1);
    573 
    574 	const Vec2		uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coordX, prec.coordBits.x(), prec.uvwBits.x());
    575 
    576 	const int		minI			= deFloorFloatToInt32(uBounds.x());
    577 	const int		maxI			= deFloorFloatToInt32(uBounds.y());
    578 
    579 	for (int i = minI; i <= maxI; i++)
    580 	{
    581 		const int					x		= wrap(sampler.wrapS, i, level.getWidth());
    582 		const Vector<ScalarType, 4>	color	= lookup<ScalarType>(level, sampler, x, coordY, 0);
    583 
    584 		if (isColorValid(prec, color, result))
    585 			return true;
    586 	}
    587 
    588 	return false;
    589 }
    590 
    591 template<typename PrecType, typename ScalarType>
    592 static bool isNearestSampleResultValid (const ConstPixelBufferAccess&		level,
    593 										const Sampler&						sampler,
    594 										const PrecType&						prec,
    595 										const Vec2&							coord,
    596 										const int							coordZ,
    597 										const Vector<ScalarType, 4>&		result)
    598 {
    599 	const Vec2		uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    600 	const Vec2		vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    601 
    602 	// Integer coordinates - without wrap mode
    603 	const int		minI			= deFloorFloatToInt32(uBounds.x());
    604 	const int		maxI			= deFloorFloatToInt32(uBounds.y());
    605 	const int		minJ			= deFloorFloatToInt32(vBounds.x());
    606 	const int		maxJ			= deFloorFloatToInt32(vBounds.y());
    607 
    608 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    609 
    610 	for (int j = minJ; j <= maxJ; j++)
    611 	{
    612 		for (int i = minI; i <= maxI; i++)
    613 		{
    614 			const int					x		= wrap(sampler.wrapS, i, level.getWidth());
    615 			const int					y		= wrap(sampler.wrapT, j, level.getHeight());
    616 			const Vector<ScalarType, 4>	color	= lookup<ScalarType>(level, sampler, x, y, coordZ);
    617 
    618 			if (isColorValid(prec, color, result))
    619 				return true;
    620 		}
    621 	}
    622 
    623 	return false;
    624 }
    625 
    626 template<typename PrecType, typename ScalarType>
    627 static bool isNearestSampleResultValid (const ConstPixelBufferAccess&		level,
    628 										const Sampler&						sampler,
    629 										const PrecType&						prec,
    630 										const Vec3&							coord,
    631 										const Vector<ScalarType, 4>&		result)
    632 {
    633 	const Vec2		uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    634 	const Vec2		vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    635 	const Vec2		wBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getDepth(),	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    636 
    637 	// Integer coordinates - without wrap mode
    638 	const int		minI			= deFloorFloatToInt32(uBounds.x());
    639 	const int		maxI			= deFloorFloatToInt32(uBounds.y());
    640 	const int		minJ			= deFloorFloatToInt32(vBounds.x());
    641 	const int		maxJ			= deFloorFloatToInt32(vBounds.y());
    642 	const int		minK			= deFloorFloatToInt32(wBounds.x());
    643 	const int		maxK			= deFloorFloatToInt32(wBounds.y());
    644 
    645 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    646 
    647 	for (int k = minK; k <= maxK; k++)
    648 	{
    649 		for (int j = minJ; j <= maxJ; j++)
    650 		{
    651 			for (int i = minI; i <= maxI; i++)
    652 			{
    653 				const int					x		= wrap(sampler.wrapS, i, level.getWidth());
    654 				const int					y		= wrap(sampler.wrapT, j, level.getHeight());
    655 				const int					z		= wrap(sampler.wrapR, k, level.getDepth());
    656 				const Vector<ScalarType, 4>	color	= lookup<ScalarType>(level, sampler, x, y, z);
    657 
    658 				if (isColorValid(prec, color, result))
    659 					return true;
    660 			}
    661 		}
    662 	}
    663 
    664 	return false;
    665 }
    666 
    667 bool isLinearSampleResultValid (const ConstPixelBufferAccess&		level,
    668 								const Sampler&						sampler,
    669 								const LookupPrecision&				prec,
    670 								const float							coordX,
    671 								const int							coordY,
    672 								const Vec4&							result)
    673 {
    674 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coordX, prec.coordBits.x(), prec.uvwBits.x());
    675 
    676 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
    677 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
    678 
    679 	const int					w				= level.getWidth();
    680 
    681 	const TextureFormat			format			= level.getFormat();
    682 	const TextureChannelClass	texClass		= getTextureChannelClass(format.type);
    683 
    684 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
    685 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
    686 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
    687 			  sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
    688 
    689 	DE_UNREF(texClass);
    690 	DE_UNREF(format);
    691 
    692 	for (int i = minI; i <= maxI; i++)
    693 	{
    694 		// Wrapped coordinates
    695 		const int	x0		= wrap(sampler.wrapS, i  , w);
    696 		const int	x1		= wrap(sampler.wrapS, i+1, w);
    697 
    698 		// Bounds for filtering factors
    699 		const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    700 		const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    701 
    702 		const Vec4	colorA	= lookup<float>(level, sampler, x0, coordY, 0);
    703 		const Vec4	colorB	= lookup<float>(level, sampler, x1, coordY, 0);
    704 
    705 		if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
    706 		{
    707 			if (isLinearRangeValid(prec, colorA, colorB, Vec2(minA, maxA), result))
    708 				return true;
    709 		}
    710 		else
    711 		{
    712 			if (isReductionValid(prec, colorA, colorB, sampler.reductionMode, result))
    713 				return true;
    714 		}
    715 	}
    716 
    717 	return false;
    718 }
    719 
    720 bool isLinearSampleResultValid (const ConstPixelBufferAccess&		level,
    721 								const Sampler&						sampler,
    722 								const LookupPrecision&				prec,
    723 								const Vec2&							coord,
    724 								const int							coordZ,
    725 								const Vec4&							result)
    726 {
    727 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    728 	const Vec2					vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    729 
    730 	// Integer coordinate bounds for (x0,y0) - without wrap mode
    731 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
    732 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
    733 	const int					minJ			= deFloorFloatToInt32(vBounds.x()-0.5f);
    734 	const int					maxJ			= deFloorFloatToInt32(vBounds.y()-0.5f);
    735 
    736 	const int					w				= level.getWidth();
    737 	const int					h				= level.getHeight();
    738 
    739 	const TextureChannelClass	texClass		= getTextureChannelClass(level.getFormat().type);
    740 	float						searchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
    741 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
    742 												  0.0f; // Step is computed for floating-point quads based on texel values.
    743 
    744 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
    745 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
    746 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
    747 			  sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
    748 
    749 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    750 
    751 	for (int j = minJ; j <= maxJ; j++)
    752 	{
    753 		for (int i = minI; i <= maxI; i++)
    754 		{
    755 			// Wrapped coordinates
    756 			const int	x0		= wrap(sampler.wrapS, i  , w);
    757 			const int	x1		= wrap(sampler.wrapS, i+1, w);
    758 			const int	y0		= wrap(sampler.wrapT, j  , h);
    759 			const int	y1		= wrap(sampler.wrapT, j+1, h);
    760 
    761 			// Bounds for filtering factors
    762 			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    763 			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    764 			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
    765 			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
    766 
    767 			ColorQuad quad;
    768 			lookupQuad(quad, level, sampler, x0, x1, y0, y1, coordZ);
    769 
    770 			if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
    771 				searchStep = computeBilinearSearchStepFromFloatQuad(prec, quad);
    772 
    773 			if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
    774 			{
    775 				if (isBilinearRangeValid(prec, quad, Vec2(minA, maxA), Vec2(minB, maxB), searchStep, result))
    776 					return true;
    777 			}
    778 			else
    779 			{
    780 				if (isReductionValid(prec, quad, sampler.reductionMode, result))
    781 					return true;
    782 			}
    783 		}
    784 	}
    785 
    786 	return false;
    787 }
    788 
    789 static bool isLinearSampleResultValid (const ConstPixelBufferAccess&		level,
    790 									   const Sampler&						sampler,
    791 									   const LookupPrecision&				prec,
    792 									   const Vec3&							coord,
    793 									   const Vec4&							result)
    794 {
    795 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    796 	const Vec2					vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    797 	const Vec2					wBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getDepth(),	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    798 
    799 	// Integer coordinate bounds for (x0,y0) - without wrap mode
    800 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
    801 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
    802 	const int					minJ			= deFloorFloatToInt32(vBounds.x()-0.5f);
    803 	const int					maxJ			= deFloorFloatToInt32(vBounds.y()-0.5f);
    804 	const int					minK			= deFloorFloatToInt32(wBounds.x()-0.5f);
    805 	const int					maxK			= deFloorFloatToInt32(wBounds.y()-0.5f);
    806 
    807 	const int					w				= level.getWidth();
    808 	const int					h				= level.getHeight();
    809 	const int					d				= level.getDepth();
    810 
    811 	const TextureChannelClass	texClass		= getTextureChannelClass(level.getFormat().type);
    812 	float						searchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
    813 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
    814 												  0.0f; // Step is computed for floating-point quads based on texel values.
    815 
    816 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
    817 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
    818 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
    819 			  sampler.reductionMode	!= Sampler::WEIGHTED_AVERAGE);
    820 
    821 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    822 
    823 	for (int k = minK; k <= maxK; k++)
    824 	{
    825 		for (int j = minJ; j <= maxJ; j++)
    826 		{
    827 			for (int i = minI; i <= maxI; i++)
    828 			{
    829 				// Wrapped coordinates
    830 				const int	x0		= wrap(sampler.wrapS, i  , w);
    831 				const int	x1		= wrap(sampler.wrapS, i+1, w);
    832 				const int	y0		= wrap(sampler.wrapT, j  , h);
    833 				const int	y1		= wrap(sampler.wrapT, j+1, h);
    834 				const int	z0		= wrap(sampler.wrapR, k  , d);
    835 				const int	z1		= wrap(sampler.wrapR, k+1, d);
    836 
    837 				// Bounds for filtering factors
    838 				const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    839 				const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    840 				const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
    841 				const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
    842 				const float	minC	= de::clamp((wBounds.x()-0.5f)-float(k), 0.0f, 1.0f);
    843 				const float	maxC	= de::clamp((wBounds.y()-0.5f)-float(k), 0.0f, 1.0f);
    844 
    845 				ColorQuad quad0, quad1;
    846 				lookupQuad(quad0, level, sampler, x0, x1, y0, y1, z0);
    847 				lookupQuad(quad1, level, sampler, x0, x1, y0, y1, z1);
    848 
    849 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
    850 					searchStep = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad0), computeBilinearSearchStepFromFloatQuad(prec, quad1));
    851 
    852 				if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
    853 				{
    854 					if (isTrilinearRangeValid(prec, quad0, quad1, Vec2(minA, maxA), Vec2(minB, maxB), Vec2(minC, maxC), searchStep, result))
    855 						return true;
    856 				}
    857 				else
    858 				{
    859 					if (isReductionValid(prec, quad0, quad1, sampler.reductionMode, result))
    860 						return true;
    861 				}
    862 			}
    863 		}
    864 	}
    865 
    866 	return false;
    867 }
    868 
    869 static bool isNearestMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    870 													const ConstPixelBufferAccess&	level1,
    871 													const Sampler&					sampler,
    872 													const LookupPrecision&			prec,
    873 													const float						coord,
    874 													const int						coordY,
    875 													const Vec2&						fBounds,
    876 													const Vec4&						result)
    877 {
    878 	const int		w0				= level0.getWidth();
    879 	const int		w1				= level1.getWidth();
    880 
    881 	const Vec2		uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord, prec.coordBits.x(), prec.uvwBits.x());
    882 	const Vec2		uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord, prec.coordBits.x(), prec.uvwBits.x());
    883 
    884 	// Integer coordinates - without wrap mode
    885 	const int		minI0			= deFloorFloatToInt32(uBounds0.x());
    886 	const int		maxI0			= deFloorFloatToInt32(uBounds0.y());
    887 	const int		minI1			= deFloorFloatToInt32(uBounds1.x());
    888 	const int		maxI1			= deFloorFloatToInt32(uBounds1.y());
    889 
    890 	for (int i0 = minI0; i0 <= maxI0; i0++)
    891 	{
    892 		for (int i1 = minI1; i1 <= maxI1; i1++)
    893 		{
    894 			const Vec4	c0	= lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), coordY, 0);
    895 			const Vec4	c1	= lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), coordY, 0);
    896 
    897 			if (isLinearRangeValid(prec, c0, c1, fBounds, result))
    898 				return true;
    899 		}
    900 	}
    901 
    902 	return false;
    903 }
    904 
    905 static bool isNearestMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    906 													const ConstPixelBufferAccess&	level1,
    907 													const Sampler&					sampler,
    908 													const LookupPrecision&			prec,
    909 													const Vec2&						coord,
    910 													const int						coordZ,
    911 													const Vec2&						fBounds,
    912 													const Vec4&						result)
    913 {
    914 	const int		w0				= level0.getWidth();
    915 	const int		w1				= level1.getWidth();
    916 	const int		h0				= level0.getHeight();
    917 	const int		h1				= level1.getHeight();
    918 
    919 	const Vec2		uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    920 	const Vec2		uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    921 	const Vec2		vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    922 	const Vec2		vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    923 
    924 	// Integer coordinates - without wrap mode
    925 	const int		minI0			= deFloorFloatToInt32(uBounds0.x());
    926 	const int		maxI0			= deFloorFloatToInt32(uBounds0.y());
    927 	const int		minI1			= deFloorFloatToInt32(uBounds1.x());
    928 	const int		maxI1			= deFloorFloatToInt32(uBounds1.y());
    929 	const int		minJ0			= deFloorFloatToInt32(vBounds0.x());
    930 	const int		maxJ0			= deFloorFloatToInt32(vBounds0.y());
    931 	const int		minJ1			= deFloorFloatToInt32(vBounds1.x());
    932 	const int		maxJ1			= deFloorFloatToInt32(vBounds1.y());
    933 
    934 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
    935 	{
    936 		for (int i0 = minI0; i0 <= maxI0; i0++)
    937 		{
    938 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
    939 			{
    940 				for (int i1 = minI1; i1 <= maxI1; i1++)
    941 				{
    942 					const Vec4	c0	= lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ);
    943 					const Vec4	c1	= lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ);
    944 
    945 					if (isLinearRangeValid(prec, c0, c1, fBounds, result))
    946 						return true;
    947 				}
    948 			}
    949 		}
    950 	}
    951 
    952 	return false;
    953 }
    954 
    955 static bool isNearestMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    956 													const ConstPixelBufferAccess&	level1,
    957 													const Sampler&					sampler,
    958 													const LookupPrecision&			prec,
    959 													const Vec3&						coord,
    960 													const Vec2&						fBounds,
    961 													const Vec4&						result)
    962 {
    963 	const int		w0				= level0.getWidth();
    964 	const int		w1				= level1.getWidth();
    965 	const int		h0				= level0.getHeight();
    966 	const int		h1				= level1.getHeight();
    967 	const int		d0				= level0.getDepth();
    968 	const int		d1				= level1.getDepth();
    969 
    970 	const Vec2		uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    971 	const Vec2		uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    972 	const Vec2		vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    973 	const Vec2		vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    974 	const Vec2		wBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d0,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    975 	const Vec2		wBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d1,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    976 
    977 	// Integer coordinates - without wrap mode
    978 	const int		minI0			= deFloorFloatToInt32(uBounds0.x());
    979 	const int		maxI0			= deFloorFloatToInt32(uBounds0.y());
    980 	const int		minI1			= deFloorFloatToInt32(uBounds1.x());
    981 	const int		maxI1			= deFloorFloatToInt32(uBounds1.y());
    982 	const int		minJ0			= deFloorFloatToInt32(vBounds0.x());
    983 	const int		maxJ0			= deFloorFloatToInt32(vBounds0.y());
    984 	const int		minJ1			= deFloorFloatToInt32(vBounds1.x());
    985 	const int		maxJ1			= deFloorFloatToInt32(vBounds1.y());
    986 	const int		minK0			= deFloorFloatToInt32(wBounds0.x());
    987 	const int		maxK0			= deFloorFloatToInt32(wBounds0.y());
    988 	const int		minK1			= deFloorFloatToInt32(wBounds1.x());
    989 	const int		maxK1			= deFloorFloatToInt32(wBounds1.y());
    990 
    991 	for (int k0 = minK0; k0 <= maxK0; k0++)
    992 	{
    993 		for (int j0 = minJ0; j0 <= maxJ0; j0++)
    994 		{
    995 			for (int i0 = minI0; i0 <= maxI0; i0++)
    996 			{
    997 				for (int k1 = minK1; k1 <= maxK1; k1++)
    998 				{
    999 					for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1000 					{
   1001 						for (int i1 = minI1; i1 <= maxI1; i1++)
   1002 						{
   1003 							const Vec4	c0	= lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), wrap(sampler.wrapR, k0, d0));
   1004 							const Vec4	c1	= lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), wrap(sampler.wrapR, k1, d1));
   1005 
   1006 							if (isLinearRangeValid(prec, c0, c1, fBounds, result))
   1007 								return true;
   1008 						}
   1009 					}
   1010 				}
   1011 			}
   1012 		}
   1013 	}
   1014 
   1015 	return false;
   1016 }
   1017 
   1018 static bool isLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
   1019 												   const ConstPixelBufferAccess&	level1,
   1020 												   const Sampler&					sampler,
   1021 												   const LookupPrecision&			prec,
   1022 												   const float						coordX,
   1023 												   const int						coordY,
   1024 												   const Vec2&						fBounds,
   1025 												   const Vec4&						result)
   1026 {
   1027 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
   1028 	//						   Right now this allows pairing any two valid bilinear quads.
   1029 
   1030 	const int					w0				= level0.getWidth();
   1031 	const int					w1				= level1.getWidth();
   1032 
   1033 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coordX, prec.coordBits.x(), prec.uvwBits.x());
   1034 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coordX, prec.coordBits.x(), prec.uvwBits.x());
   1035 
   1036 	// Integer coordinates - without wrap mode
   1037 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1038 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1039 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1040 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1041 
   1042 	const TextureChannelClass	texClass		= getTextureChannelClass(level0.getFormat().type);
   1043 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1044 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1045 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1046 
   1047 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
   1048 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
   1049 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
   1050 			  sampler.reductionMode	!= Sampler::WEIGHTED_AVERAGE);
   1051 
   1052 	for (int i0 = minI0; i0 <= maxI0; i0++)
   1053 	{
   1054 		ColorLine	line0;
   1055 		float		searchStep0;
   1056 
   1057 		{
   1058 			const int	x0		= wrap(sampler.wrapS, i0  , w0);
   1059 			const int	x1		= wrap(sampler.wrapS, i0+1, w0);
   1060 			lookupLine(line0, level0, sampler, x0, x1, coordY);
   1061 
   1062 			if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1063 				searchStep0 = computeBilinearSearchStepFromFloatLine(prec, line0);
   1064 			else
   1065 				searchStep0 = cSearchStep;
   1066 		}
   1067 
   1068 		const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1069 		const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1070 
   1071 		for (int i1 = minI1; i1 <= maxI1; i1++)
   1072 		{
   1073 			ColorLine	line1;
   1074 			float		searchStep1;
   1075 
   1076 			{
   1077 				const int	x0		= wrap(sampler.wrapS, i1  , w1);
   1078 				const int	x1		= wrap(sampler.wrapS, i1+1, w1);
   1079 				lookupLine(line1, level1, sampler, x0, x1, coordY);
   1080 
   1081 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1082 					searchStep1 = computeBilinearSearchStepFromFloatLine(prec, line1);
   1083 				else
   1084 					searchStep1 = cSearchStep;
   1085 			}
   1086 
   1087 			const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1088 			const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1089 
   1090 			if (is1DTrilinearFilterResultValid(prec, line0, line1, Vec2(minA0, maxA0), Vec2(minA1, maxA1), fBounds, de::min(searchStep0, searchStep1), result))
   1091 				return true;
   1092 		}
   1093 	}
   1094 
   1095 	return false;
   1096 }
   1097 
   1098 static bool isLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
   1099 												   const ConstPixelBufferAccess&	level1,
   1100 												   const Sampler&					sampler,
   1101 												   const LookupPrecision&			prec,
   1102 												   const Vec2&						coord,
   1103 												   const int						coordZ,
   1104 												   const Vec2&						fBounds,
   1105 												   const Vec4&						result)
   1106 {
   1107 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
   1108 	//						   Right now this allows pairing any two valid bilinear quads.
   1109 
   1110 	const int					w0				= level0.getWidth();
   1111 	const int					w1				= level1.getWidth();
   1112 	const int					h0				= level0.getHeight();
   1113 	const int					h1				= level1.getHeight();
   1114 
   1115 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1116 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1117 	const Vec2					vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1118 	const Vec2					vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1119 
   1120 	// Integer coordinates - without wrap mode
   1121 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1122 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1123 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1124 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1125 	const int					minJ0			= deFloorFloatToInt32(vBounds0.x()-0.5f);
   1126 	const int					maxJ0			= deFloorFloatToInt32(vBounds0.y()-0.5f);
   1127 	const int					minJ1			= deFloorFloatToInt32(vBounds1.x()-0.5f);
   1128 	const int					maxJ1			= deFloorFloatToInt32(vBounds1.y()-0.5f);
   1129 
   1130 	const TextureChannelClass	texClass		= getTextureChannelClass(level0.getFormat().type);
   1131 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1132 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1133 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1134 
   1135 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
   1136 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
   1137 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
   1138 			  sampler.reductionMode	!= Sampler::WEIGHTED_AVERAGE);
   1139 
   1140 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
   1141 	{
   1142 		for (int i0 = minI0; i0 <= maxI0; i0++)
   1143 		{
   1144 			ColorQuad	quad0;
   1145 			float		searchStep0;
   1146 
   1147 			{
   1148 				const int	x0		= wrap(sampler.wrapS, i0  , w0);
   1149 				const int	x1		= wrap(sampler.wrapS, i0+1, w0);
   1150 				const int	y0		= wrap(sampler.wrapT, j0  , h0);
   1151 				const int	y1		= wrap(sampler.wrapT, j0+1, h0);
   1152 				lookupQuad(quad0, level0, sampler, x0, x1, y0, y1, coordZ);
   1153 
   1154 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1155 					searchStep0 = computeBilinearSearchStepFromFloatQuad(prec, quad0);
   1156 				else
   1157 					searchStep0 = cSearchStep;
   1158 			}
   1159 
   1160 			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1161 			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1162 			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
   1163 			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
   1164 
   1165 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1166 			{
   1167 				for (int i1 = minI1; i1 <= maxI1; i1++)
   1168 				{
   1169 					ColorQuad	quad1;
   1170 					float		searchStep1;
   1171 
   1172 					{
   1173 						const int	x0		= wrap(sampler.wrapS, i1  , w1);
   1174 						const int	x1		= wrap(sampler.wrapS, i1+1, w1);
   1175 						const int	y0		= wrap(sampler.wrapT, j1  , h1);
   1176 						const int	y1		= wrap(sampler.wrapT, j1+1, h1);
   1177 						lookupQuad(quad1, level1, sampler, x0, x1, y0, y1, coordZ);
   1178 
   1179 						if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1180 							searchStep1 = computeBilinearSearchStepFromFloatQuad(prec, quad1);
   1181 						else
   1182 							searchStep1 = cSearchStep;
   1183 					}
   1184 
   1185 					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1186 					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1187 					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
   1188 					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
   1189 
   1190 					if (is2DTrilinearFilterResultValid(prec, quad0, quad1, Vec2(minA0, maxA0), Vec2(minB0, maxB0), Vec2(minA1, maxA1), Vec2(minB1, maxB1),
   1191 													   fBounds, de::min(searchStep0, searchStep1), result))
   1192 						return true;
   1193 				}
   1194 			}
   1195 		}
   1196 	}
   1197 
   1198 	return false;
   1199 }
   1200 
   1201 static bool isLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
   1202 												   const ConstPixelBufferAccess&	level1,
   1203 												   const Sampler&					sampler,
   1204 												   const LookupPrecision&			prec,
   1205 												   const Vec3&						coord,
   1206 												   const Vec2&						fBounds,
   1207 												   const Vec4&						result)
   1208 {
   1209 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
   1210 	//						   Right now this allows pairing any two valid bilinear quads.
   1211 
   1212 	const int					w0				= level0.getWidth();
   1213 	const int					w1				= level1.getWidth();
   1214 	const int					h0				= level0.getHeight();
   1215 	const int					h1				= level1.getHeight();
   1216 	const int					d0				= level0.getDepth();
   1217 	const int					d1				= level1.getDepth();
   1218 
   1219 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1220 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1221 	const Vec2					vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1222 	const Vec2					vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1223 	const Vec2					wBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d0,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
   1224 	const Vec2					wBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d1,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
   1225 
   1226 	// Integer coordinates - without wrap mode
   1227 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1228 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1229 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1230 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1231 	const int					minJ0			= deFloorFloatToInt32(vBounds0.x()-0.5f);
   1232 	const int					maxJ0			= deFloorFloatToInt32(vBounds0.y()-0.5f);
   1233 	const int					minJ1			= deFloorFloatToInt32(vBounds1.x()-0.5f);
   1234 	const int					maxJ1			= deFloorFloatToInt32(vBounds1.y()-0.5f);
   1235 	const int					minK0			= deFloorFloatToInt32(wBounds0.x()-0.5f);
   1236 	const int					maxK0			= deFloorFloatToInt32(wBounds0.y()-0.5f);
   1237 	const int					minK1			= deFloorFloatToInt32(wBounds1.x()-0.5f);
   1238 	const int					maxK1			= deFloorFloatToInt32(wBounds1.y()-0.5f);
   1239 
   1240 	const TextureChannelClass	texClass		= getTextureChannelClass(level0.getFormat().type);
   1241 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1242 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1243 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1244 
   1245 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
   1246 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
   1247 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
   1248 			  sampler.reductionMode	!= Sampler::WEIGHTED_AVERAGE);
   1249 
   1250 	for (int k0 = minK0; k0 <= maxK0; k0++)
   1251 	{
   1252 		for (int j0 = minJ0; j0 <= maxJ0; j0++)
   1253 		{
   1254 			for (int i0 = minI0; i0 <= maxI0; i0++)
   1255 			{
   1256 				ColorQuad	quad00, quad01;
   1257 				float		searchStep0;
   1258 
   1259 				{
   1260 					const int	x0		= wrap(sampler.wrapS, i0  , w0);
   1261 					const int	x1		= wrap(sampler.wrapS, i0+1, w0);
   1262 					const int	y0		= wrap(sampler.wrapT, j0  , h0);
   1263 					const int	y1		= wrap(sampler.wrapT, j0+1, h0);
   1264 					const int	z0		= wrap(sampler.wrapR, k0  , d0);
   1265 					const int	z1		= wrap(sampler.wrapR, k0+1, d0);
   1266 					lookupQuad(quad00, level0, sampler, x0, x1, y0, y1, z0);
   1267 					lookupQuad(quad01, level0, sampler, x0, x1, y0, y1, z1);
   1268 
   1269 					if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1270 						searchStep0 = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad00), computeBilinearSearchStepFromFloatQuad(prec, quad01));
   1271 					else
   1272 						searchStep0 = cSearchStep;
   1273 				}
   1274 
   1275 				const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1276 				const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1277 				const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
   1278 				const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
   1279 				const float	minC0	= de::clamp((wBounds0.x()-0.5f)-float(k0), 0.0f, 1.0f);
   1280 				const float	maxC0	= de::clamp((wBounds0.y()-0.5f)-float(k0), 0.0f, 1.0f);
   1281 
   1282 				for (int k1 = minK1; k1 <= maxK1; k1++)
   1283 				{
   1284 					for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1285 					{
   1286 						for (int i1 = minI1; i1 <= maxI1; i1++)
   1287 						{
   1288 							ColorQuad	quad10, quad11;
   1289 							float		searchStep1;
   1290 
   1291 							{
   1292 								const int	x0		= wrap(sampler.wrapS, i1  , w1);
   1293 								const int	x1		= wrap(sampler.wrapS, i1+1, w1);
   1294 								const int	y0		= wrap(sampler.wrapT, j1  , h1);
   1295 								const int	y1		= wrap(sampler.wrapT, j1+1, h1);
   1296 								const int	z0		= wrap(sampler.wrapR, k1  , d1);
   1297 								const int	z1		= wrap(sampler.wrapR, k1+1, d1);
   1298 								lookupQuad(quad10, level1, sampler, x0, x1, y0, y1, z0);
   1299 								lookupQuad(quad11, level1, sampler, x0, x1, y0, y1, z1);
   1300 
   1301 								if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1302 									searchStep1 = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad10), computeBilinearSearchStepFromFloatQuad(prec, quad11));
   1303 								else
   1304 									searchStep1 = cSearchStep;
   1305 							}
   1306 
   1307 							const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1308 							const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1309 							const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
   1310 							const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
   1311 							const float	minC1	= de::clamp((wBounds1.x()-0.5f)-float(k1), 0.0f, 1.0f);
   1312 							const float	maxC1	= de::clamp((wBounds1.y()-0.5f)-float(k1), 0.0f, 1.0f);
   1313 
   1314 							if (is3DTrilinearFilterResultValid(prec, quad00, quad01, quad10, quad11,
   1315 															   Vec2(minA0, maxA0), Vec2(minB0, maxB0), Vec2(minC0, maxC0),
   1316 															   Vec2(minA1, maxA1), Vec2(minB1, maxB1), Vec2(minC1, maxC1),
   1317 															   fBounds, de::min(searchStep0, searchStep1), result))
   1318 								return true;
   1319 						}
   1320 					}
   1321 				}
   1322 			}
   1323 		}
   1324 	}
   1325 
   1326 	return false;
   1327 }
   1328 
   1329 static bool isLevelSampleResultValid (const ConstPixelBufferAccess&		level,
   1330 									  const Sampler&					sampler,
   1331 									  const Sampler::FilterMode			filterMode,
   1332 									  const LookupPrecision&			prec,
   1333 									  const float						coordX,
   1334 									  const int							coordY,
   1335 									  const Vec4&						result)
   1336 {
   1337 	if (filterMode == Sampler::LINEAR)
   1338 		return isLinearSampleResultValid(level, sampler, prec, coordX, coordY, result);
   1339 	else
   1340 		return isNearestSampleResultValid(level, sampler, prec, coordX, coordY, result);
   1341 }
   1342 
   1343 static bool isLevelSampleResultValid (const ConstPixelBufferAccess&		level,
   1344 									  const Sampler&					sampler,
   1345 									  const Sampler::FilterMode			filterMode,
   1346 									  const LookupPrecision&			prec,
   1347 									  const Vec2&						coord,
   1348 									  const int							coordZ,
   1349 									  const Vec4&						result)
   1350 {
   1351 	if (filterMode == Sampler::LINEAR)
   1352 		return isLinearSampleResultValid(level, sampler, prec, coord, coordZ, result);
   1353 	else
   1354 		return isNearestSampleResultValid(level, sampler, prec, coord, coordZ, result);
   1355 }
   1356 
   1357 static bool isMipmapLinearSampleResultValid (const ConstPixelBufferAccess&		level0,
   1358 										     const ConstPixelBufferAccess&		level1,
   1359 											 const Sampler&						sampler,
   1360 										     const Sampler::FilterMode			levelFilter,
   1361 										     const LookupPrecision&				prec,
   1362 										     const float						coordX,
   1363 											 const int							coordY,
   1364 										     const Vec2&						fBounds,
   1365 										     const Vec4&						result)
   1366 {
   1367 	if (levelFilter == Sampler::LINEAR)
   1368 		return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coordX, coordY, fBounds, result);
   1369 	else
   1370 		return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coordX, coordY, fBounds, result);
   1371 }
   1372 
   1373 static bool isMipmapLinearSampleResultValid (const ConstPixelBufferAccess&		level0,
   1374 										     const ConstPixelBufferAccess&		level1,
   1375 											 const Sampler&						sampler,
   1376 										     const Sampler::FilterMode			levelFilter,
   1377 										     const LookupPrecision&				prec,
   1378 										     const Vec2&						coord,
   1379 											 const int							coordZ,
   1380 										     const Vec2&						fBounds,
   1381 										     const Vec4&						result)
   1382 {
   1383 	if (levelFilter == Sampler::LINEAR)
   1384 		return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, result);
   1385 	else
   1386 		return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, result);
   1387 }
   1388 
   1389 bool isLookupResultValid (const Texture2DView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec2& coord, const Vec2& lodBounds, const Vec4& result)
   1390 {
   1391 	const float		minLod			= lodBounds.x();
   1392 	const float		maxLod			= lodBounds.y();
   1393 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1394 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1395 
   1396 	DE_ASSERT(isSamplerSupported(sampler));
   1397 
   1398 	if (canBeMagnified)
   1399 	{
   1400 		if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result))
   1401 			return true;
   1402 	}
   1403 
   1404 	if (canBeMinified)
   1405 	{
   1406 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1407 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1408 		const int	minTexLevel		= 0;
   1409 		const int	maxTexLevel		= texture.getNumLevels()-1;
   1410 
   1411 		DE_ASSERT(minTexLevel <= maxTexLevel);
   1412 
   1413 		if (isLinearMipmap && minTexLevel < maxTexLevel)
   1414 		{
   1415 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1416 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1417 
   1418 			DE_ASSERT(minLevel <= maxLevel);
   1419 
   1420 			for (int level = minLevel; level <= maxLevel; level++)
   1421 			{
   1422 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1423 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1424 
   1425 				if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), result))
   1426 					return true;
   1427 			}
   1428 		}
   1429 		else if (isNearestMipmap)
   1430 		{
   1431 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1432 			//		 decision to allow floor(lod + 0.5) as well.
   1433 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1434 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1435 
   1436 			DE_ASSERT(minLevel <= maxLevel);
   1437 
   1438 			for (int level = minLevel; level <= maxLevel; level++)
   1439 			{
   1440 				if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, result))
   1441 					return true;
   1442 			}
   1443 		}
   1444 		else
   1445 		{
   1446 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result))
   1447 				return true;
   1448 		}
   1449 	}
   1450 
   1451 	return false;
   1452 }
   1453 
   1454 bool isLookupResultValid (const Texture1DView& texture, const Sampler& sampler, const LookupPrecision& prec, const float coord, const Vec2& lodBounds, const Vec4& result)
   1455 {
   1456 	const float		minLod			= lodBounds.x();
   1457 	const float		maxLod			= lodBounds.y();
   1458 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1459 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1460 
   1461 	DE_ASSERT(isSamplerSupported(sampler));
   1462 
   1463 	if (canBeMagnified)
   1464 	{
   1465 		if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result))
   1466 			return true;
   1467 	}
   1468 
   1469 	if (canBeMinified)
   1470 	{
   1471 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1472 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1473 		const int	minTexLevel		= 0;
   1474 		const int	maxTexLevel		= texture.getNumLevels()-1;
   1475 
   1476 		DE_ASSERT(minTexLevel <= maxTexLevel);
   1477 
   1478 		if (isLinearMipmap && minTexLevel < maxTexLevel)
   1479 		{
   1480 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1481 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1482 
   1483 			DE_ASSERT(minLevel <= maxLevel);
   1484 
   1485 			for (int level = minLevel; level <= maxLevel; level++)
   1486 			{
   1487 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1488 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1489 
   1490 				if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), result))
   1491 					return true;
   1492 			}
   1493 		}
   1494 		else if (isNearestMipmap)
   1495 		{
   1496 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1497 			//		 decision to allow floor(lod + 0.5) as well.
   1498 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1499 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1500 
   1501 			DE_ASSERT(minLevel <= maxLevel);
   1502 
   1503 			for (int level = minLevel; level <= maxLevel; level++)
   1504 			{
   1505 				if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, result))
   1506 					return true;
   1507 			}
   1508 		}
   1509 		else
   1510 		{
   1511 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result))
   1512 				return true;
   1513 		}
   1514 	}
   1515 
   1516 	return false;
   1517 }
   1518 
   1519 static bool isSeamlessLinearSampleResultValid (const ConstPixelBufferAccess (&faces)[CUBEFACE_LAST],
   1520 											   const Sampler&				sampler,
   1521 											   const LookupPrecision&		prec,
   1522 											   const CubeFaceFloatCoords&	coords,
   1523 											   const Vec4&					result)
   1524 {
   1525 	const int					size			= faces[coords.face].getWidth();
   1526 
   1527 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1528 	const Vec2					vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1529 
   1530 	// Integer coordinate bounds for (x0,y0) - without wrap mode
   1531 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
   1532 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
   1533 	const int					minJ			= deFloorFloatToInt32(vBounds.x()-0.5f);
   1534 	const int					maxJ			= deFloorFloatToInt32(vBounds.y()-0.5f);
   1535 
   1536 	const TextureChannelClass	texClass		= getTextureChannelClass(faces[coords.face].getFormat().type);
   1537 	float						searchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1538 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1539 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1540 
   1541 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
   1542 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
   1543 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
   1544 			  sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
   1545 
   1546 	for (int j = minJ; j <= maxJ; j++)
   1547 	{
   1548 		for (int i = minI; i <= maxI; i++)
   1549 		{
   1550 			const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+0)), size);
   1551 			const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+0)), size);
   1552 			const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+1)), size);
   1553 			const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+1)), size);
   1554 
   1555 			// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   1556 			// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
   1557 			if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1558 				return true;
   1559 
   1560 			// Bounds for filtering factors
   1561 			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
   1562 			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
   1563 			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
   1564 			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
   1565 
   1566 			ColorQuad quad;
   1567 			quad.p00 = lookup<float>(faces[c00.face], sampler, c00.s, c00.t, 0);
   1568 			quad.p10 = lookup<float>(faces[c10.face], sampler, c10.s, c10.t, 0);
   1569 			quad.p01 = lookup<float>(faces[c01.face], sampler, c01.s, c01.t, 0);
   1570 			quad.p11 = lookup<float>(faces[c11.face], sampler, c11.s, c11.t, 0);
   1571 
   1572 			if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1573 				searchStep = computeBilinearSearchStepFromFloatQuad(prec, quad);
   1574 
   1575 			if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
   1576 			{
   1577 				if (isBilinearRangeValid(prec, quad, Vec2(minA, maxA), Vec2(minB, maxB), searchStep, result))
   1578 					return true;
   1579 			}
   1580 			else
   1581 			{
   1582 				if (isReductionValid(prec, quad, sampler.reductionMode, result))
   1583 					return true;
   1584 			}
   1585 		}
   1586 	}
   1587 
   1588 	return false;
   1589 }
   1590 
   1591 static bool isSeamplessLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess	(&faces0)[CUBEFACE_LAST],
   1592 															const ConstPixelBufferAccess	(&faces1)[CUBEFACE_LAST],
   1593 															const Sampler&					sampler,
   1594 															const LookupPrecision&			prec,
   1595 															const CubeFaceFloatCoords&		coords,
   1596 															const Vec2&						fBounds,
   1597 															const Vec4&						result)
   1598 {
   1599 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
   1600 	//						   Right now this allows pairing any two valid bilinear quads.
   1601 
   1602 	const int					size0			= faces0[coords.face].getWidth();
   1603 	const int					size1			= faces1[coords.face].getWidth();
   1604 
   1605 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1606 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1607 	const Vec2					vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1608 	const Vec2					vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1609 
   1610 	// Integer coordinates - without wrap mode
   1611 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1612 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1613 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1614 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1615 	const int					minJ0			= deFloorFloatToInt32(vBounds0.x()-0.5f);
   1616 	const int					maxJ0			= deFloorFloatToInt32(vBounds0.y()-0.5f);
   1617 	const int					minJ1			= deFloorFloatToInt32(vBounds1.x()-0.5f);
   1618 	const int					maxJ1			= deFloorFloatToInt32(vBounds1.y()-0.5f);
   1619 
   1620 	const TextureChannelClass	texClass		= getTextureChannelClass(faces0[coords.face].getFormat().type);
   1621 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1622 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1623 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1624 
   1625 	DE_ASSERT(texClass				== TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	||
   1626 			  texClass				== TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	||
   1627 			  texClass				== TEXTURECHANNELCLASS_FLOATING_POINT		||
   1628 			  sampler.reductionMode	!= Sampler::WEIGHTED_AVERAGE);
   1629 
   1630 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
   1631 	{
   1632 		for (int i0 = minI0; i0 <= maxI0; i0++)
   1633 		{
   1634 			ColorQuad	quad0;
   1635 			float		searchStep0;
   1636 
   1637 			{
   1638 				const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+0)), size0);
   1639 				const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+0)), size0);
   1640 				const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+1)), size0);
   1641 				const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+1)), size0);
   1642 
   1643 				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   1644 				// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
   1645 				if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1646 					return true;
   1647 
   1648 				quad0.p00 = lookup<float>(faces0[c00.face], sampler, c00.s, c00.t, 0);
   1649 				quad0.p10 = lookup<float>(faces0[c10.face], sampler, c10.s, c10.t, 0);
   1650 				quad0.p01 = lookup<float>(faces0[c01.face], sampler, c01.s, c01.t, 0);
   1651 				quad0.p11 = lookup<float>(faces0[c11.face], sampler, c11.s, c11.t, 0);
   1652 
   1653 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1654 					searchStep0 = computeBilinearSearchStepFromFloatQuad(prec, quad0);
   1655 				else
   1656 					searchStep0 = cSearchStep;
   1657 			}
   1658 
   1659 			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1660 			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1661 			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
   1662 			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
   1663 
   1664 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1665 			{
   1666 				for (int i1 = minI1; i1 <= maxI1; i1++)
   1667 				{
   1668 					ColorQuad	quad1;
   1669 					float		searchStep1;
   1670 
   1671 					{
   1672 						const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+0)), size1);
   1673 						const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+0)), size1);
   1674 						const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+1)), size1);
   1675 						const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+1)), size1);
   1676 
   1677 						if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1678 							return true;
   1679 
   1680 						quad1.p00 = lookup<float>(faces1[c00.face], sampler, c00.s, c00.t, 0);
   1681 						quad1.p10 = lookup<float>(faces1[c10.face], sampler, c10.s, c10.t, 0);
   1682 						quad1.p01 = lookup<float>(faces1[c01.face], sampler, c01.s, c01.t, 0);
   1683 						quad1.p11 = lookup<float>(faces1[c11.face], sampler, c11.s, c11.t, 0);
   1684 
   1685 						if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1686 							searchStep1 = computeBilinearSearchStepFromFloatQuad(prec, quad1);
   1687 						else
   1688 							searchStep1 = cSearchStep;
   1689 					}
   1690 
   1691 					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1692 					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1693 					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
   1694 					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
   1695 
   1696 					if (is2DTrilinearFilterResultValid(prec, quad0, quad1, Vec2(minA0, maxA0), Vec2(minB0, maxB0), Vec2(minA1, maxA1), Vec2(minB1, maxB1),
   1697 													   fBounds, de::min(searchStep0, searchStep1), result))
   1698 						return true;
   1699 				}
   1700 			}
   1701 		}
   1702 	}
   1703 
   1704 	return false;
   1705 }
   1706 
   1707 static bool isCubeLevelSampleResultValid (const ConstPixelBufferAccess		(&level)[CUBEFACE_LAST],
   1708 										  const Sampler&					sampler,
   1709 										  const Sampler::FilterMode			filterMode,
   1710 										  const LookupPrecision&			prec,
   1711 										  const CubeFaceFloatCoords&		coords,
   1712 										  const Vec4&						result)
   1713 {
   1714 	if (filterMode == Sampler::LINEAR)
   1715 	{
   1716 		if (sampler.seamlessCubeMap)
   1717 			return isSeamlessLinearSampleResultValid(level, sampler, prec, coords, result);
   1718 		else
   1719 			return isLinearSampleResultValid(level[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, result);
   1720 	}
   1721 	else
   1722 		return isNearestSampleResultValid(level[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, result);
   1723 }
   1724 
   1725 static bool isCubeMipmapLinearSampleResultValid (const ConstPixelBufferAccess	(&faces0)[CUBEFACE_LAST],
   1726 												 const ConstPixelBufferAccess	(&faces1)[CUBEFACE_LAST],
   1727 												 const Sampler&					sampler,
   1728 												 const Sampler::FilterMode		levelFilter,
   1729 												 const LookupPrecision&			prec,
   1730 												 const CubeFaceFloatCoords&		coords,
   1731 												 const Vec2&					fBounds,
   1732 												 const Vec4&					result)
   1733 {
   1734 	if (levelFilter == Sampler::LINEAR)
   1735 	{
   1736 		if (sampler.seamlessCubeMap)
   1737 			return isSeamplessLinearMipmapLinearSampleResultValid(faces0, faces1, sampler, prec, coords, fBounds, result);
   1738 		else
   1739 			return isLinearMipmapLinearSampleResultValid(faces0[coords.face], faces1[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, result);
   1740 	}
   1741 	else
   1742 		return isNearestMipmapLinearSampleResultValid(faces0[coords.face], faces1[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, result);
   1743 }
   1744 
   1745 static void getCubeLevelFaces (const TextureCubeView& texture, const int levelNdx, ConstPixelBufferAccess (&out)[CUBEFACE_LAST])
   1746 {
   1747 	for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
   1748 		out[faceNdx] = texture.getLevelFace(levelNdx, (CubeFace)faceNdx);
   1749 }
   1750 
   1751 bool isLookupResultValid (const TextureCubeView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec3& coord, const Vec2& lodBounds, const Vec4& result)
   1752 {
   1753 	int			numPossibleFaces				= 0;
   1754 	CubeFace	possibleFaces[CUBEFACE_LAST];
   1755 
   1756 	DE_ASSERT(isSamplerSupported(sampler));
   1757 
   1758 	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   1759 
   1760 	if (numPossibleFaces == 0)
   1761 		return true; // Result is undefined.
   1762 
   1763 	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   1764 	{
   1765 		const CubeFaceFloatCoords	faceCoords		(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
   1766 		const float					minLod			= lodBounds.x();
   1767 		const float					maxLod			= lodBounds.y();
   1768 		const bool					canBeMagnified	= minLod <= sampler.lodThreshold;
   1769 		const bool					canBeMinified	= maxLod > sampler.lodThreshold;
   1770 
   1771 		if (canBeMagnified)
   1772 		{
   1773 			ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1774 			getCubeLevelFaces(texture, 0, faces);
   1775 
   1776 			if (isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result))
   1777 				return true;
   1778 		}
   1779 
   1780 		if (canBeMinified)
   1781 		{
   1782 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1783 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1784 			const int	minTexLevel		= 0;
   1785 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1786 
   1787 			DE_ASSERT(minTexLevel <= maxTexLevel);
   1788 
   1789 			if (isLinearMipmap && minTexLevel < maxTexLevel)
   1790 			{
   1791 				const int	minLevel	= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1792 				const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1793 
   1794 				DE_ASSERT(minLevel <= maxLevel);
   1795 
   1796 				for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   1797 				{
   1798 					const float				minF	= de::clamp(minLod - float(levelNdx), 0.0f, 1.0f);
   1799 					const float				maxF	= de::clamp(maxLod - float(levelNdx), 0.0f, 1.0f);
   1800 
   1801 					ConstPixelBufferAccess	faces0[CUBEFACE_LAST];
   1802 					ConstPixelBufferAccess	faces1[CUBEFACE_LAST];
   1803 
   1804 					getCubeLevelFaces(texture, levelNdx,		faces0);
   1805 					getCubeLevelFaces(texture, levelNdx + 1,	faces1);
   1806 
   1807 					if (isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), result))
   1808 						return true;
   1809 				}
   1810 			}
   1811 			else if (isNearestMipmap)
   1812 			{
   1813 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1814 				//		 decision to allow floor(lod + 0.5) as well.
   1815 				const int	minLevel	= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1816 				const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1817 
   1818 				DE_ASSERT(minLevel <= maxLevel);
   1819 
   1820 				for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   1821 				{
   1822 					ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1823 					getCubeLevelFaces(texture, levelNdx, faces);
   1824 
   1825 					if (isCubeLevelSampleResultValid(faces, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, result))
   1826 						return true;
   1827 				}
   1828 			}
   1829 			else
   1830 			{
   1831 				ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1832 				getCubeLevelFaces(texture, 0, faces);
   1833 
   1834 				if (isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result))
   1835 					return true;
   1836 			}
   1837 		}
   1838 	}
   1839 
   1840 	return false;
   1841 }
   1842 
   1843 static inline IVec2 computeLayerRange (int numLayers, int numCoordBits, float layerCoord)
   1844 {
   1845 	const float	err		= computeFloatingPointError(layerCoord, numCoordBits);
   1846 	const int	minL	= (int)deFloatFloor(layerCoord - err + 0.5f);		// Round down
   1847 	const int	maxL	= (int)deFloatCeil(layerCoord + err + 0.5f) - 1;	// Round up
   1848 
   1849 	DE_ASSERT(minL <= maxL);
   1850 
   1851 	return IVec2(de::clamp(minL, 0, numLayers-1), de::clamp(maxL, 0, numLayers-1));
   1852 }
   1853 
   1854 bool isLookupResultValid (const Texture1DArrayView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec2& coord, const Vec2& lodBounds, const Vec4& result)
   1855 {
   1856 	const IVec2		layerRange		= computeLayerRange(texture.getNumLayers(), prec.coordBits.y(), coord.y());
   1857 	const float		coordX			= coord.x();
   1858 	const float		minLod			= lodBounds.x();
   1859 	const float		maxLod			= lodBounds.y();
   1860 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1861 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1862 
   1863 	DE_ASSERT(isSamplerSupported(sampler));
   1864 
   1865 	for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
   1866 	{
   1867 		if (canBeMagnified)
   1868 		{
   1869 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coordX, layer, result))
   1870 				return true;
   1871 		}
   1872 
   1873 		if (canBeMinified)
   1874 		{
   1875 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1876 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1877 			const int	minTexLevel		= 0;
   1878 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1879 
   1880 			DE_ASSERT(minTexLevel <= maxTexLevel);
   1881 
   1882 			if (isLinearMipmap && minTexLevel < maxTexLevel)
   1883 			{
   1884 				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1885 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1886 
   1887 				DE_ASSERT(minLevel <= maxLevel);
   1888 
   1889 				for (int level = minLevel; level <= maxLevel; level++)
   1890 				{
   1891 					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1892 					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1893 
   1894 					if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coordX, layer, Vec2(minF, maxF), result))
   1895 						return true;
   1896 				}
   1897 			}
   1898 			else if (isNearestMipmap)
   1899 			{
   1900 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1901 				//		 decision to allow floor(lod + 0.5) as well.
   1902 				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1903 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1904 
   1905 				DE_ASSERT(minLevel <= maxLevel);
   1906 
   1907 				for (int level = minLevel; level <= maxLevel; level++)
   1908 				{
   1909 					if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coordX, layer, result))
   1910 						return true;
   1911 				}
   1912 			}
   1913 			else
   1914 			{
   1915 				if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coordX, layer, result))
   1916 					return true;
   1917 			}
   1918 		}
   1919 	}
   1920 
   1921 	return false;
   1922 }
   1923 
   1924 bool isLookupResultValid (const Texture2DArrayView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec3& coord, const Vec2& lodBounds, const Vec4& result)
   1925 {
   1926 	const IVec2		layerRange		= computeLayerRange(texture.getNumLayers(), prec.coordBits.z(), coord.z());
   1927 	const Vec2		coordXY			= coord.swizzle(0,1);
   1928 	const float		minLod			= lodBounds.x();
   1929 	const float		maxLod			= lodBounds.y();
   1930 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1931 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1932 
   1933 	DE_ASSERT(isSamplerSupported(sampler));
   1934 
   1935 	for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
   1936 	{
   1937 		if (canBeMagnified)
   1938 		{
   1939 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coordXY, layer, result))
   1940 				return true;
   1941 		}
   1942 
   1943 		if (canBeMinified)
   1944 		{
   1945 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1946 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1947 			const int	minTexLevel		= 0;
   1948 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1949 
   1950 			DE_ASSERT(minTexLevel <= maxTexLevel);
   1951 
   1952 			if (isLinearMipmap && minTexLevel < maxTexLevel)
   1953 			{
   1954 				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1955 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1956 
   1957 				DE_ASSERT(minLevel <= maxLevel);
   1958 
   1959 				for (int level = minLevel; level <= maxLevel; level++)
   1960 				{
   1961 					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1962 					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1963 
   1964 					if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coordXY, layer, Vec2(minF, maxF), result))
   1965 						return true;
   1966 				}
   1967 			}
   1968 			else if (isNearestMipmap)
   1969 			{
   1970 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1971 				//		 decision to allow floor(lod + 0.5) as well.
   1972 				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1973 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1974 
   1975 				DE_ASSERT(minLevel <= maxLevel);
   1976 
   1977 				for (int level = minLevel; level <= maxLevel; level++)
   1978 				{
   1979 					if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coordXY, layer, result))
   1980 						return true;
   1981 				}
   1982 			}
   1983 			else
   1984 			{
   1985 				if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coordXY, layer, result))
   1986 					return true;
   1987 			}
   1988 		}
   1989 	}
   1990 
   1991 	return false;
   1992 }
   1993 
   1994 static bool isLevelSampleResultValid (const ConstPixelBufferAccess&		level,
   1995 									  const Sampler&					sampler,
   1996 									  const Sampler::FilterMode			filterMode,
   1997 									  const LookupPrecision&			prec,
   1998 									  const Vec3&						coord,
   1999 									  const Vec4&						result)
   2000 {
   2001 	if (filterMode == Sampler::LINEAR)
   2002 		return isLinearSampleResultValid(level, sampler, prec, coord, result);
   2003 	else
   2004 		return isNearestSampleResultValid(level, sampler, prec, coord, result);
   2005 }
   2006 
   2007 static bool isMipmapLinearSampleResultValid (const ConstPixelBufferAccess&		level0,
   2008 										     const ConstPixelBufferAccess&		level1,
   2009 											 const Sampler&						sampler,
   2010 										     const Sampler::FilterMode			levelFilter,
   2011 										     const LookupPrecision&				prec,
   2012 										     const Vec3&						coord,
   2013 										     const Vec2&						fBounds,
   2014 										     const Vec4&						result)
   2015 {
   2016 	if (levelFilter == Sampler::LINEAR)
   2017 		return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, fBounds, result);
   2018 	else
   2019 		return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, fBounds, result);
   2020 }
   2021 
   2022 bool isLookupResultValid (const Texture3DView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec3& coord, const Vec2& lodBounds, const Vec4& result)
   2023 {
   2024 	const float		minLod			= lodBounds.x();
   2025 	const float		maxLod			= lodBounds.y();
   2026 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   2027 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   2028 
   2029 	DE_ASSERT(isSamplerSupported(sampler));
   2030 
   2031 	if (canBeMagnified)
   2032 	{
   2033 		if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, result))
   2034 			return true;
   2035 	}
   2036 
   2037 	if (canBeMinified)
   2038 	{
   2039 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   2040 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   2041 		const int	minTexLevel		= 0;
   2042 		const int	maxTexLevel		= texture.getNumLevels()-1;
   2043 
   2044 		DE_ASSERT(minTexLevel <= maxTexLevel);
   2045 
   2046 		if (isLinearMipmap && minTexLevel < maxTexLevel)
   2047 		{
   2048 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   2049 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   2050 
   2051 			DE_ASSERT(minLevel <= maxLevel);
   2052 
   2053 			for (int level = minLevel; level <= maxLevel; level++)
   2054 			{
   2055 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   2056 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   2057 
   2058 				if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, Vec2(minF, maxF), result))
   2059 					return true;
   2060 			}
   2061 		}
   2062 		else if (isNearestMipmap)
   2063 		{
   2064 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   2065 			//		 decision to allow floor(lod + 0.5) as well.
   2066 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   2067 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   2068 
   2069 			DE_ASSERT(minLevel <= maxLevel);
   2070 
   2071 			for (int level = minLevel; level <= maxLevel; level++)
   2072 			{
   2073 				if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, result))
   2074 					return true;
   2075 			}
   2076 		}
   2077 		else
   2078 		{
   2079 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, result))
   2080 				return true;
   2081 		}
   2082 	}
   2083 
   2084 	return false;
   2085 }
   2086 
   2087 static void getCubeArrayLevelFaces (const TextureCubeArrayView& texture, const int levelNdx, const int layerNdx, ConstPixelBufferAccess (&out)[CUBEFACE_LAST])
   2088 {
   2089 	const ConstPixelBufferAccess&	level		= texture.getLevel(levelNdx);
   2090 	const int						layerDepth	= layerNdx * 6;
   2091 
   2092 	for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
   2093 	{
   2094 		const CubeFace face = (CubeFace)faceNdx;
   2095 		out[faceNdx] = getSubregion(level, 0, 0, layerDepth + getCubeArrayFaceIndex(face), level.getWidth(), level.getHeight(), 1);
   2096 	}
   2097 }
   2098 
   2099 bool isLookupResultValid (const TextureCubeArrayView& texture, const Sampler& sampler, const LookupPrecision& prec, const IVec4& coordBits, const Vec4& coord, const Vec2& lodBounds, const Vec4& result)
   2100 {
   2101 	const IVec2	layerRange						= computeLayerRange(texture.getNumLayers(), coordBits.w(), coord.w());
   2102 	const Vec3	layerCoord						= coord.toWidth<3>();
   2103 	int			numPossibleFaces				= 0;
   2104 	CubeFace	possibleFaces[CUBEFACE_LAST];
   2105 
   2106 	DE_ASSERT(isSamplerSupported(sampler));
   2107 
   2108 	getPossibleCubeFaces(layerCoord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   2109 
   2110 	if (numPossibleFaces == 0)
   2111 		return true; // Result is undefined.
   2112 
   2113 	for (int layerNdx = layerRange.x(); layerNdx <= layerRange.y(); layerNdx++)
   2114 	{
   2115 		for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   2116 		{
   2117 			const CubeFaceFloatCoords	faceCoords		(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], layerCoord));
   2118 			const float					minLod			= lodBounds.x();
   2119 			const float					maxLod			= lodBounds.y();
   2120 			const bool					canBeMagnified	= minLod <= sampler.lodThreshold;
   2121 			const bool					canBeMinified	= maxLod > sampler.lodThreshold;
   2122 
   2123 			if (canBeMagnified)
   2124 			{
   2125 				ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2126 				getCubeArrayLevelFaces(texture, 0, layerNdx, faces);
   2127 
   2128 				if (isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result))
   2129 					return true;
   2130 			}
   2131 
   2132 			if (canBeMinified)
   2133 			{
   2134 				const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   2135 				const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   2136 				const int	minTexLevel		= 0;
   2137 				const int	maxTexLevel		= texture.getNumLevels()-1;
   2138 
   2139 				DE_ASSERT(minTexLevel <= maxTexLevel);
   2140 
   2141 				if (isLinearMipmap && minTexLevel < maxTexLevel)
   2142 				{
   2143 					const int	minLevel	= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   2144 					const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   2145 
   2146 					DE_ASSERT(minLevel <= maxLevel);
   2147 
   2148 					for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   2149 					{
   2150 						const float		minF	= de::clamp(minLod - float(levelNdx), 0.0f, 1.0f);
   2151 						const float		maxF	= de::clamp(maxLod - float(levelNdx), 0.0f, 1.0f);
   2152 
   2153 						ConstPixelBufferAccess	faces0[CUBEFACE_LAST];
   2154 						ConstPixelBufferAccess	faces1[CUBEFACE_LAST];
   2155 
   2156 						getCubeArrayLevelFaces(texture, levelNdx,		layerNdx,	faces0);
   2157 						getCubeArrayLevelFaces(texture, levelNdx + 1,	layerNdx,	faces1);
   2158 
   2159 						if (isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), result))
   2160 							return true;
   2161 					}
   2162 				}
   2163 				else if (isNearestMipmap)
   2164 				{
   2165 					// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   2166 					//		 decision to allow floor(lod + 0.5) as well.
   2167 					const int	minLevel	= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   2168 					const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   2169 
   2170 					DE_ASSERT(minLevel <= maxLevel);
   2171 
   2172 					for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   2173 					{
   2174 						ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2175 						getCubeArrayLevelFaces(texture, levelNdx, layerNdx, faces);
   2176 
   2177 						if (isCubeLevelSampleResultValid(faces, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, result))
   2178 							return true;
   2179 					}
   2180 				}
   2181 				else
   2182 				{
   2183 					ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2184 					getCubeArrayLevelFaces(texture, 0, layerNdx, faces);
   2185 
   2186 					if (isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result))
   2187 						return true;
   2188 				}
   2189 			}
   2190 		}
   2191 	}
   2192 
   2193 	return false;
   2194 }
   2195 
   2196 Vec4 computeFixedPointThreshold (const IVec4& bits)
   2197 {
   2198 	return computeFixedPointError(bits);
   2199 }
   2200 
   2201 Vec4 computeFloatingPointThreshold (const IVec4& bits, const Vec4& value)
   2202 {
   2203 	return computeFloatingPointError(value, bits);
   2204 }
   2205 
   2206 Vec2 computeLodBoundsFromDerivates (const float dudx, const float dvdx, const float dwdx, const float dudy, const float dvdy, const float dwdy, const LodPrecision& prec)
   2207 {
   2208 	const float		mux			= deFloatAbs(dudx);
   2209 	const float		mvx			= deFloatAbs(dvdx);
   2210 	const float		mwx			= deFloatAbs(dwdx);
   2211 	const float		muy			= deFloatAbs(dudy);
   2212 	const float		mvy			= deFloatAbs(dvdy);
   2213 	const float		mwy			= deFloatAbs(dwdy);
   2214 
   2215 	// Ideal:
   2216 	// px = deFloatSqrt2(mux*mux + mvx*mvx + mwx*mwx);
   2217 	// py = deFloatSqrt2(muy*muy + mvy*mvy + mwy*mwy);
   2218 
   2219 	// fx, fy estimate lower bounds
   2220 	const float		fxMin		= de::max(de::max(mux, mvx), mwx);
   2221 	const float		fyMin		= de::max(de::max(muy, mvy), mwy);
   2222 
   2223 	// fx, fy estimate upper bounds
   2224 	const float		sqrt2		= deFloatSqrt(2.0f);
   2225 	const float		fxMax		= sqrt2 * (mux + mvx + mwx);
   2226 	const float		fyMax		= sqrt2 * (muy + mvy + mwy);
   2227 
   2228 	// p = max(px, py) (isotropic filtering)
   2229 	const float		pMin		= de::max(fxMin, fyMin);
   2230 	const float		pMax		= de::max(fxMax, fyMax);
   2231 
   2232 	// error terms
   2233 	const float		pMinErr		= computeFloatingPointError(pMin, prec.derivateBits);
   2234 	const float		pMaxErr		= computeFloatingPointError(pMax, prec.derivateBits);
   2235 
   2236 	const float		minLod		= deFloatLog2(pMin-pMinErr);
   2237 	const float		maxLod		= deFloatLog2(pMax+pMaxErr);
   2238 	const float		lodErr		= computeFixedPointError(prec.lodBits);
   2239 
   2240 	DE_ASSERT(minLod <= maxLod);
   2241 	return Vec2(minLod-lodErr, maxLod+lodErr);
   2242 }
   2243 
   2244 Vec2 computeLodBoundsFromDerivates (const float dudx, const float dvdx, const float dudy, const float dvdy, const LodPrecision& prec)
   2245 {
   2246 	return computeLodBoundsFromDerivates(dudx, dvdx, 0.0f, dudy, dvdy, 0.0f, prec);
   2247 }
   2248 
   2249 Vec2 computeLodBoundsFromDerivates (const float dudx, const float dudy, const LodPrecision& prec)
   2250 {
   2251 	return computeLodBoundsFromDerivates(dudx, 0.0f, 0.0f, dudy, 0.0f, 0.0f, prec);
   2252 }
   2253 
   2254 Vec2 computeCubeLodBoundsFromDerivates (const Vec3& coord, const Vec3& coordDx, const Vec3& coordDy, const int faceSize, const LodPrecision& prec)
   2255 {
   2256 	const bool			allowBrokenEdgeDerivate		= false;
   2257 	const CubeFace		face						= selectCubeFace(coord);
   2258 	int					maNdx						= 0;
   2259 	int					sNdx						= 0;
   2260 	int					tNdx						= 0;
   2261 
   2262 	// \note Derivate signs don't matter when computing lod
   2263 	switch (face)
   2264 	{
   2265 		case CUBEFACE_NEGATIVE_X:
   2266 		case CUBEFACE_POSITIVE_X: maNdx = 0; sNdx = 2; tNdx = 1; break;
   2267 		case CUBEFACE_NEGATIVE_Y:
   2268 		case CUBEFACE_POSITIVE_Y: maNdx = 1; sNdx = 0; tNdx = 2; break;
   2269 		case CUBEFACE_NEGATIVE_Z:
   2270 		case CUBEFACE_POSITIVE_Z: maNdx = 2; sNdx = 0; tNdx = 1; break;
   2271 		default:
   2272 			DE_ASSERT(DE_FALSE);
   2273 	}
   2274 
   2275 	{
   2276 		const float		sc		= coord[sNdx];
   2277 		const float		tc		= coord[tNdx];
   2278 		const float		ma		= de::abs(coord[maNdx]);
   2279 		const float		scdx	= coordDx[sNdx];
   2280 		const float		tcdx	= coordDx[tNdx];
   2281 		const float		madx	= de::abs(coordDx[maNdx]);
   2282 		const float		scdy	= coordDy[sNdx];
   2283 		const float		tcdy	= coordDy[tNdx];
   2284 		const float		mady	= de::abs(coordDy[maNdx]);
   2285 		const float		dudx	= float(faceSize) * 0.5f * (scdx*ma - sc*madx) / (ma*ma);
   2286 		const float		dvdx	= float(faceSize) * 0.5f * (tcdx*ma - tc*madx) / (ma*ma);
   2287 		const float		dudy	= float(faceSize) * 0.5f * (scdy*ma - sc*mady) / (ma*ma);
   2288 		const float		dvdy	= float(faceSize) * 0.5f * (tcdy*ma - tc*mady) / (ma*ma);
   2289 		const Vec2		bounds	= computeLodBoundsFromDerivates(dudx, dvdx, dudy, dvdy, prec);
   2290 
   2291 		// Implementations may compute derivate from projected (s, t) resulting in incorrect values at edges.
   2292 		if (allowBrokenEdgeDerivate)
   2293 		{
   2294 			const Vec3			dxErr		= computeFloatingPointError(coordDx, IVec3(prec.derivateBits));
   2295 			const Vec3			dyErr		= computeFloatingPointError(coordDy, IVec3(prec.derivateBits));
   2296 			const Vec3			xoffs		= abs(coordDx) + dxErr;
   2297 			const Vec3			yoffs		= abs(coordDy) + dyErr;
   2298 
   2299 			if (selectCubeFace(coord + xoffs) != face ||
   2300 				selectCubeFace(coord - xoffs) != face ||
   2301 				selectCubeFace(coord + yoffs) != face ||
   2302 				selectCubeFace(coord - yoffs) != face)
   2303 			{
   2304 				return Vec2(bounds.x(), 1000.0f);
   2305 			}
   2306 		}
   2307 
   2308 		return bounds;
   2309 	}
   2310 }
   2311 
   2312 Vec2 clampLodBounds (const Vec2& lodBounds, const Vec2& lodMinMax, const LodPrecision& prec)
   2313 {
   2314 	const float lodErr	= computeFixedPointError(prec.lodBits);
   2315 	const float	a		= lodMinMax.x();
   2316 	const float	b		= lodMinMax.y();
   2317 	return Vec2(de::clamp(lodBounds.x(), a-lodErr, b-lodErr), de::clamp(lodBounds.y(), a+lodErr, b+lodErr));
   2318 }
   2319 
   2320 bool isLevel1DLookupResultValid (const ConstPixelBufferAccess&	access,
   2321 								 const Sampler&					sampler,
   2322 								 TexLookupScaleMode				scaleMode,
   2323 								 const LookupPrecision&			prec,
   2324 								 const float					coordX,
   2325 								 const int						coordY,
   2326 								 const Vec4&					result)
   2327 {
   2328 	const Sampler::FilterMode filterMode = scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
   2329 	return isLevelSampleResultValid(access, sampler, filterMode, prec, coordX, coordY, result);
   2330 }
   2331 
   2332 bool isLevel1DLookupResultValid (const ConstPixelBufferAccess&	access,
   2333 								 const Sampler&					sampler,
   2334 								 TexLookupScaleMode				scaleMode,
   2335 								 const IntLookupPrecision&		prec,
   2336 								 const float					coordX,
   2337 								 const int						coordY,
   2338 								 const IVec4&					result)
   2339 {
   2340 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2341 	DE_UNREF(scaleMode);
   2342 	return isNearestSampleResultValid(access, sampler, prec, coordX, coordY, result);
   2343 }
   2344 
   2345 bool isLevel1DLookupResultValid (const ConstPixelBufferAccess&	access,
   2346 								 const Sampler&					sampler,
   2347 								 TexLookupScaleMode				scaleMode,
   2348 								 const IntLookupPrecision&		prec,
   2349 								 const float					coordX,
   2350 								 const int						coordY,
   2351 								 const UVec4&					result)
   2352 {
   2353 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2354 	DE_UNREF(scaleMode);
   2355 	return isNearestSampleResultValid(access, sampler, prec, coordX, coordY, result);
   2356 }
   2357 
   2358 bool isLevel2DLookupResultValid (const ConstPixelBufferAccess&	access,
   2359 								 const Sampler&					sampler,
   2360 								 TexLookupScaleMode				scaleMode,
   2361 								 const LookupPrecision&			prec,
   2362 								 const Vec2&					coord,
   2363 								 const int						coordZ,
   2364 								 const Vec4&					result)
   2365 {
   2366 	const Sampler::FilterMode filterMode = scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
   2367 	return isLevelSampleResultValid(access, sampler, filterMode, prec, coord, coordZ, result);
   2368 }
   2369 
   2370 bool isLevel2DLookupResultValid (const ConstPixelBufferAccess&	access,
   2371 								 const Sampler&					sampler,
   2372 								 TexLookupScaleMode				scaleMode,
   2373 								 const IntLookupPrecision&		prec,
   2374 								 const Vec2&					coord,
   2375 								 const int						coordZ,
   2376 								 const IVec4&					result)
   2377 {
   2378 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2379 	DE_UNREF(scaleMode);
   2380 	return isNearestSampleResultValid(access, sampler, prec, coord, coordZ, result);
   2381 }
   2382 
   2383 bool isLevel2DLookupResultValid (const ConstPixelBufferAccess&	access,
   2384 								 const Sampler&					sampler,
   2385 								 TexLookupScaleMode				scaleMode,
   2386 								 const IntLookupPrecision&		prec,
   2387 								 const Vec2&					coord,
   2388 								 const int						coordZ,
   2389 								 const UVec4&					result)
   2390 {
   2391 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2392 	DE_UNREF(scaleMode);
   2393 	return isNearestSampleResultValid(access, sampler, prec, coord, coordZ, result);
   2394 }
   2395 
   2396 bool isLevel3DLookupResultValid (const ConstPixelBufferAccess&	access,
   2397 								 const Sampler&					sampler,
   2398 								 TexLookupScaleMode				scaleMode,
   2399 								 const LookupPrecision&			prec,
   2400 								 const Vec3&					coord,
   2401 								 const Vec4&					result)
   2402 {
   2403 	const tcu::Sampler::FilterMode filterMode = scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
   2404 	return isLevelSampleResultValid(access, sampler, filterMode, prec, coord, result);
   2405 }
   2406 
   2407 bool isLevel3DLookupResultValid (const ConstPixelBufferAccess&	access,
   2408 								 const Sampler&					sampler,
   2409 								 TexLookupScaleMode				scaleMode,
   2410 								 const IntLookupPrecision&		prec,
   2411 								 const Vec3&					coord,
   2412 								 const IVec4&					result)
   2413 {
   2414 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2415 	DE_UNREF(scaleMode);
   2416 	return isNearestSampleResultValid(access, sampler, prec, coord, result);
   2417 }
   2418 
   2419 bool isLevel3DLookupResultValid (const ConstPixelBufferAccess&	access,
   2420 								 const Sampler&					sampler,
   2421 								 TexLookupScaleMode				scaleMode,
   2422 								 const IntLookupPrecision&		prec,
   2423 								 const Vec3&					coord,
   2424 								 const UVec4&					result)
   2425 {
   2426 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2427 	DE_UNREF(scaleMode);
   2428 	return isNearestSampleResultValid(access, sampler, prec, coord, result);
   2429 }
   2430 
   2431 template<typename PrecType, typename ScalarType>
   2432 static bool isGatherOffsetsResultValid (const ConstPixelBufferAccess&	level,
   2433 										const Sampler&					sampler,
   2434 										const PrecType&					prec,
   2435 										const Vec2&						coord,
   2436 										int								coordZ,
   2437 										int								componentNdx,
   2438 										const IVec2						(&offsets)[4],
   2439 										const Vector<ScalarType, 4>&	result)
   2440 {
   2441 	const Vec2	uBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   2442 	const Vec2	vBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   2443 
   2444 	// Integer coordinate bounds for (x0, y0) - without wrap mode
   2445 	const int	minI		= deFloorFloatToInt32(uBounds.x()-0.5f);
   2446 	const int	maxI		= deFloorFloatToInt32(uBounds.y()-0.5f);
   2447 	const int	minJ		= deFloorFloatToInt32(vBounds.x()-0.5f);
   2448 	const int	maxJ		= deFloorFloatToInt32(vBounds.y()-0.5f);
   2449 
   2450 	const int	w			= level.getWidth();
   2451 	const int	h			= level.getHeight();
   2452 
   2453 	for (int j = minJ; j <= maxJ; j++)
   2454 	{
   2455 		for (int i = minI; i <= maxI; i++)
   2456 		{
   2457 			Vector<ScalarType, 4> color;
   2458 			for (int offNdx = 0; offNdx < 4; offNdx++)
   2459 			{
   2460 				// offNdx-th coordinate offset and then wrapped.
   2461 				const int x = wrap(sampler.wrapS, i+offsets[offNdx].x(), w);
   2462 				const int y = wrap(sampler.wrapT, j+offsets[offNdx].y(), h);
   2463 				color[offNdx] = lookup<ScalarType>(level, sampler, x, y, coordZ)[componentNdx];
   2464 			}
   2465 
   2466 			if (isColorValid(prec, color, result))
   2467 				return true;
   2468 		}
   2469 	}
   2470 
   2471 	return false;
   2472 }
   2473 
   2474 bool isGatherOffsetsResultValid (const Texture2DView&			texture,
   2475 								 const Sampler&					sampler,
   2476 								 const LookupPrecision&			prec,
   2477 								 const Vec2&					coord,
   2478 								 int							componentNdx,
   2479 								 const IVec2					(&offsets)[4],
   2480 								 const Vec4&					result)
   2481 {
   2482 	return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
   2483 }
   2484 
   2485 bool isGatherOffsetsResultValid (const Texture2DView&			texture,
   2486 								 const Sampler&					sampler,
   2487 								 const IntLookupPrecision&		prec,
   2488 								 const Vec2&					coord,
   2489 								 int							componentNdx,
   2490 								 const IVec2					(&offsets)[4],
   2491 								 const IVec4&					result)
   2492 {
   2493 	return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
   2494 }
   2495 
   2496 bool isGatherOffsetsResultValid (const Texture2DView&			texture,
   2497 								 const Sampler&					sampler,
   2498 								 const IntLookupPrecision&		prec,
   2499 								 const Vec2&					coord,
   2500 								 int							componentNdx,
   2501 								 const IVec2					(&offsets)[4],
   2502 								 const UVec4&					result)
   2503 {
   2504 	return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
   2505 }
   2506 
   2507 template <typename PrecType, typename ScalarType>
   2508 static bool is2DArrayGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2509 											   const Sampler&					sampler,
   2510 											   const PrecType&					prec,
   2511 											   const Vec3&						coord,
   2512 											   int								componentNdx,
   2513 											   const IVec2						(&offsets)[4],
   2514 											   const Vector<ScalarType, 4>&		result)
   2515 {
   2516 	const IVec2 layerRange = computeLayerRange(texture.getNumLayers(), prec.coordBits.z(), coord.z());
   2517 	for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
   2518 	{
   2519 		if (isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0,1), layer, componentNdx, offsets, result))
   2520 			return true;
   2521 	}
   2522 	return false;
   2523 }
   2524 
   2525 bool isGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2526 								 const Sampler&					sampler,
   2527 								 const LookupPrecision&			prec,
   2528 								 const Vec3&					coord,
   2529 								 int							componentNdx,
   2530 								 const IVec2					(&offsets)[4],
   2531 								 const Vec4&					result)
   2532 {
   2533 	return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
   2534 }
   2535 
   2536 bool isGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2537 								 const Sampler&					sampler,
   2538 								 const IntLookupPrecision&		prec,
   2539 								 const Vec3&					coord,
   2540 								 int							componentNdx,
   2541 								 const IVec2					(&offsets)[4],
   2542 								 const IVec4&					result)
   2543 {
   2544 	return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
   2545 }
   2546 
   2547 bool isGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2548 								 const Sampler&					sampler,
   2549 								 const IntLookupPrecision&		prec,
   2550 								 const Vec3&					coord,
   2551 								 int							componentNdx,
   2552 								 const IVec2					(&offsets)[4],
   2553 								 const UVec4&					result)
   2554 {
   2555 	return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
   2556 }
   2557 
   2558 template<typename PrecType, typename ScalarType>
   2559 static bool isGatherResultValid (const TextureCubeView&			texture,
   2560 								 const Sampler&					sampler,
   2561 								 const PrecType&				prec,
   2562 								 const CubeFaceFloatCoords&		coords,
   2563 								 int							componentNdx,
   2564 								 const Vector<ScalarType, 4>&	result)
   2565 {
   2566 	const int	size		= texture.getLevelFace(0, coords.face).getWidth();
   2567 
   2568 	const Vec2	uBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
   2569 	const Vec2	vBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
   2570 
   2571 	// Integer coordinate bounds for (x0,y0) - without wrap mode
   2572 	const int	minI		= deFloorFloatToInt32(uBounds.x()-0.5f);
   2573 	const int	maxI		= deFloorFloatToInt32(uBounds.y()-0.5f);
   2574 	const int	minJ		= deFloorFloatToInt32(vBounds.x()-0.5f);
   2575 	const int	maxJ		= deFloorFloatToInt32(vBounds.y()-0.5f);
   2576 
   2577 	// Face accesses
   2578 	ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2579 	for (int face = 0; face < CUBEFACE_LAST; face++)
   2580 		faces[face] = texture.getLevelFace(0, CubeFace(face));
   2581 
   2582 	for (int j = minJ; j <= maxJ; j++)
   2583 	{
   2584 		for (int i = minI; i <= maxI; i++)
   2585 		{
   2586 			static const IVec2 offsets[4] =
   2587 			{
   2588 				IVec2(0, 1),
   2589 				IVec2(1, 1),
   2590 				IVec2(1, 0),
   2591 				IVec2(0, 0)
   2592 			};
   2593 
   2594 			Vector<ScalarType, 4> color;
   2595 			for (int offNdx = 0; offNdx < 4; offNdx++)
   2596 			{
   2597 				const CubeFaceIntCoords c = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, i+offsets[offNdx].x(), j+offsets[offNdx].y()), size);
   2598 				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   2599 				// \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color.
   2600 				//							 See also isSeamlessLinearSampleResultValid and similar.
   2601 				if (c.face == CUBEFACE_LAST)
   2602 					return true;
   2603 
   2604 				color[offNdx] = lookup<ScalarType>(faces[c.face], sampler, c.s, c.t, 0)[componentNdx];
   2605 			}
   2606 
   2607 			if (isColorValid(prec, color, result))
   2608 				return true;
   2609 		}
   2610 	}
   2611 
   2612 	return false;
   2613 }
   2614 
   2615 template <typename PrecType, typename ScalarType>
   2616 static bool isCubeGatherResultValid (const TextureCubeView&			texture,
   2617 									 const Sampler&					sampler,
   2618 									 const PrecType&				prec,
   2619 									 const Vec3&					coord,
   2620 									 int							componentNdx,
   2621 									 const Vector<ScalarType, 4>&	result)
   2622 {
   2623 	int			numPossibleFaces				= 0;
   2624 	CubeFace	possibleFaces[CUBEFACE_LAST];
   2625 
   2626 	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   2627 
   2628 	if (numPossibleFaces == 0)
   2629 		return true; // Result is undefined.
   2630 
   2631 	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   2632 	{
   2633 		const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
   2634 
   2635 		if (isGatherResultValid(texture, sampler, prec, faceCoords, componentNdx, result))
   2636 			return true;
   2637 	}
   2638 
   2639 	return false;
   2640 }
   2641 
   2642 bool isGatherResultValid (const TextureCubeView&	texture,
   2643 						  const Sampler&			sampler,
   2644 						  const LookupPrecision&	prec,
   2645 						  const Vec3&				coord,
   2646 						  int						componentNdx,
   2647 						  const Vec4&				result)
   2648 {
   2649 	return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
   2650 }
   2651 
   2652 bool isGatherResultValid (const TextureCubeView&		texture,
   2653 						  const Sampler&				sampler,
   2654 						  const IntLookupPrecision&		prec,
   2655 						  const Vec3&					coord,
   2656 						  int							componentNdx,
   2657 						  const IVec4&					result)
   2658 {
   2659 	return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
   2660 }
   2661 
   2662 bool isGatherResultValid (const TextureCubeView&		texture,
   2663 						  const Sampler&				sampler,
   2664 						  const IntLookupPrecision&		prec,
   2665 						  const Vec3&					coord,
   2666 						  int							componentNdx,
   2667 						  const UVec4&					result)
   2668 {
   2669 	return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
   2670 }
   2671 
   2672 } // tcu
   2673