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