1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 3 * ---------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Texture lookup simulator that is capable of verifying generic 22 * lookup results based on accuracy parameters. 23 *//*--------------------------------------------------------------------*/ 24 25 #include "tcuTexLookupVerifier.hpp" 26 #include "tcuTexVerifierUtil.hpp" 27 #include "tcuVectorUtil.hpp" 28 #include "tcuTextureUtil.hpp" 29 #include "deMath.h" 30 31 namespace tcu 32 { 33 34 using namespace TexVerifierUtil; 35 36 // Generic utilities 37 38 #if defined(DE_DEBUG) 39 static bool isSamplerSupported (const Sampler& sampler) 40 { 41 return sampler.compare == Sampler::COMPAREMODE_NONE && 42 isWrapModeSupported(sampler.wrapS) && 43 isWrapModeSupported(sampler.wrapT) && 44 isWrapModeSupported(sampler.wrapR); 45 } 46 #endif // DE_DEBUG 47 48 // Color read & compare utilities 49 50 static inline bool coordsInBounds (const ConstPixelBufferAccess& access, int x, int y, int z) 51 { 52 return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) && de::inBounds(z, 0, access.getDepth()); 53 } 54 55 static inline bool isSRGB (TextureFormat format) 56 { 57 return format.order == TextureFormat::sRGB || format.order == TextureFormat::sRGBA; 58 } 59 60 template<typename ScalarType> 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<> 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 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 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 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 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 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> 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> 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 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 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 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 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 198 static inline Vec4 min (const ColorLine& line) 199 { 200 return min(line.p0, line.p1); 201 } 202 203 static inline Vec4 max (const ColorLine& line) 204 { 205 return max(line.p0, line.p1); 206 } 207 208 static inline Vec4 min (const ColorQuad& quad) 209 { 210 return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11))); 211 } 212 213 static inline Vec4 max (const ColorQuad& quad) 214 { 215 return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11))); 216 } 217 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 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 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 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 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 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 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 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 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 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> 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> 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> 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 2048 Vec4 computeFixedPointThreshold (const IVec4& bits) 2049 { 2050 return computeFixedPointError(bits); 2051 } 2052 2053 Vec4 computeFloatingPointThreshold (const IVec4& bits, const Vec4& value) 2054 { 2055 return computeFloatingPointError(value, bits); 2056 } 2057 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 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 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 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 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 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 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 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 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 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 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 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 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 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> 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 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 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 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> 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 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 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 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> 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> 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 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 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 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