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