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