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