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