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)
isSamplerSupported(const Sampler & sampler)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 
coordsInBounds(const ConstPixelBufferAccess & access,int x,int y,int z)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>
lookup(const ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)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<>
lookup(const ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)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 
isColorValid(const LookupPrecision & prec,const Vec4 & ref,const Vec4 & result)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 
isColorValid(const IntLookupPrecision & prec,const IVec4 & ref,const IVec4 & result)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 
isColorValid(const IntLookupPrecision & prec,const UVec4 & ref,const UVec4 & result)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 
lookupQuad(ColorQuad & dst,const ConstPixelBufferAccess & level,const Sampler & sampler,int x0,int x1,int y0,int y1,int z)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 
lookupLine(ColorLine & dst,const ConstPixelBufferAccess & level,const Sampler & sampler,int x0,int x1,int y)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>
minComp(const Vector<T,Size> & vec)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>
maxComp(const Vector<T,Size> & vec)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 
computeBilinearSearchStepFromFloatLine(const LookupPrecision & prec,const ColorLine & line)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 
computeBilinearSearchStepFromFloatQuad(const LookupPrecision & prec,const ColorQuad & quad)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 
computeBilinearSearchStepForUnorm(const LookupPrecision & prec)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 
computeBilinearSearchStepForSnorm(const LookupPrecision & prec)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 
min(const ColorLine & line)193 static inline Vec4 min (const ColorLine& line)
194 {
195 	return min(line.p0, line.p1);
196 }
197 
max(const ColorLine & line)198 static inline Vec4 max (const ColorLine& line)
199 {
200 	return max(line.p0, line.p1);
201 }
202 
min(const ColorQuad & quad)203 static inline Vec4 min (const ColorQuad& quad)
204 {
205 	return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11)));
206 }
207 
max(const ColorQuad & quad)208 static inline Vec4 max (const ColorQuad& quad)
209 {
210 	return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11)));
211 }
212 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad,const Vec4 & result)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 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec4 & result)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 
isInColorBounds(const LookupPrecision & prec,const ColorLine & line0,const ColorLine & line1,const Vec4 & result)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 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad00,const ColorQuad & quad01,const ColorQuad & quad10,const ColorQuad & quad11,const Vec4 & result)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 
isLinearRangeValid(const LookupPrecision & prec,const Vec4 & c0,const Vec4 & c1,const Vec2 & fBounds,const Vec4 & result)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 
isBilinearRangeValid(const LookupPrecision & prec,const ColorQuad & quad,const Vec2 & xBounds,const Vec2 & yBounds,const float searchStep,const Vec4 & result)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 
isTrilinearRangeValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec2 & xBounds,const Vec2 & yBounds,const Vec2 & zBounds,const float searchStep,const Vec4 & result)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 
is1DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorLine & line0,const ColorLine & line1,const Vec2 & xBounds0,const Vec2 & xBounds1,const Vec2 & zBounds,const float searchStep,const Vec4 & result)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 
is2DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & zBounds,const float searchStep,const Vec4 & result)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 
is3DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorQuad & quad00,const ColorQuad & quad01,const ColorQuad & quad10,const ColorQuad & quad11,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & zBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & zBounds1,const Vec2 & wBounds,const float searchStep,const Vec4 & result)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>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const float coordX,const int coordY,const Vector<ScalarType,4> & result)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>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec2 & coord,const int coordZ,const Vector<ScalarType,4> & result)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>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,const Vector<ScalarType,4> & result)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 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)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 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)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 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)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 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const float coord,const int coordY,const Vec2 & fBounds,const Vec4 & result)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 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)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 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)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 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const float coordX,const int coordY,const Vec2 & fBounds,const Vec4 & result)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 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)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 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)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 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)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 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)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 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const float coordX,const int coordY,const Vec2 & fBounds,const Vec4 & result)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 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)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 
isLookupResultValid(const Texture2DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
isLookupResultValid(const Texture1DView & texture,const Sampler & sampler,const LookupPrecision & prec,const float coord,const Vec2 & lodBounds,const Vec4 & result)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 
isSeamlessLinearSampleResultValid(const ConstPixelBufferAccess (& faces)[CUBEFACE_LAST],const Sampler & sampler,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec4 & result)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 
isSeamplessLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess (& faces0)[CUBEFACE_LAST],const ConstPixelBufferAccess (& faces1)[CUBEFACE_LAST],const Sampler & sampler,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const Vec4 & result)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 
isCubeLevelSampleResultValid(const ConstPixelBufferAccess (& level)[CUBEFACE_LAST],const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec4 & result)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 
isCubeMipmapLinearSampleResultValid(const ConstPixelBufferAccess (& faces0)[CUBEFACE_LAST],const ConstPixelBufferAccess (& faces1)[CUBEFACE_LAST],const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const Vec4 & result)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 
getCubeLevelFaces(const TextureCubeView & texture,const int levelNdx,ConstPixelBufferAccess (& out)[CUBEFACE_LAST])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 
isLookupResultValid(const TextureCubeView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
computeLayerRange(int numLayers,int numCoordBits,float layerCoord)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 
isLookupResultValid(const Texture1DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
isLookupResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)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 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)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 
isLookupResultValid(const Texture3DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
getCubeArrayLevelFaces(const TextureCubeArrayView & texture,const int levelNdx,const int layerNdx,ConstPixelBufferAccess (& out)[CUBEFACE_LAST])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 
isLookupResultValid(const TextureCubeArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const IVec4 & coordBits,const Vec4 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
computeFixedPointThreshold(const IVec4 & bits)2043 Vec4 computeFixedPointThreshold (const IVec4& bits)
2044 {
2045 	return computeFixedPointError(bits);
2046 }
2047 
computeFloatingPointThreshold(const IVec4 & bits,const Vec4 & value)2048 Vec4 computeFloatingPointThreshold (const IVec4& bits, const Vec4& value)
2049 {
2050 	return computeFloatingPointError(value, bits);
2051 }
2052 
computeLodBoundsFromDerivates(const float dudx,const float dvdx,const float dwdx,const float dudy,const float dvdy,const float dwdy,const LodPrecision & prec)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 
computeLodBoundsFromDerivates(const float dudx,const float dvdx,const float dudy,const float dvdy,const LodPrecision & prec)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 
computeLodBoundsFromDerivates(const float dudx,const float dudy,const LodPrecision & prec)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 
computeCubeLodBoundsFromDerivates(const Vec3 & coord,const Vec3 & coordDx,const Vec3 & coordDy,const int faceSize,const LodPrecision & prec)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 
clampLodBounds(const Vec2 & lodBounds,const Vec2 & lodMinMax,const LodPrecision & prec)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 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)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 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const float coordX,const int coordY,const IVec4 & result)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 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const float coordX,const int coordY,const UVec4 & result)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 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)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 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec2 & coord,const int coordZ,const IVec4 & result)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 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec2 & coord,const int coordZ,const UVec4 & result)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 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)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 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec3 & coord,const IVec4 & result)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 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec3 & coord,const UVec4 & result)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>
isGatherOffsetsResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec2 & coord,int coordZ,int componentNdx,const IVec2 (& offsets)[4],const Vector<ScalarType,4> & result)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 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vec4 & result)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 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const IVec4 & result)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 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const UVec4 & result)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>
is2DArrayGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vector<ScalarType,4> & result)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 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vec4 & result)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 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const IVec4 & result)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 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const UVec4 & result)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>
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const PrecType & prec,const CubeFaceFloatCoords & coords,int componentNdx,const Vector<ScalarType,4> & result)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>
isCubeGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const Vector<ScalarType,4> & result)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 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,int componentNdx,const Vec4 & result)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 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec4 & result)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 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const UVec4 & result)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