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