1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 Google Inc.
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 GPU image sample verification
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktSampleVerifierUtil.hpp"
25
26 #include "deMath.h"
27 #include "tcuDefs.hpp"
28 #include "tcuFloat.hpp"
29 #include "tcuFloatFormat.hpp"
30 #include "tcuInterval.hpp"
31 #include "tcuTexture.hpp"
32 #include "tcuTextureUtil.hpp"
33
34 namespace vkt
35 {
36 namespace texture
37 {
38 namespace util
39 {
40
41 using namespace tcu;
42 using namespace vk;
43
mod(const deInt32 a,const deInt32 n)44 deInt32 mod (const deInt32 a, const deInt32 n)
45 {
46 const deInt32 result = a % n;
47
48 return (result < 0) ? result + n : result;
49 }
50
mirror(const deInt32 n)51 deInt32 mirror (const deInt32 n)
52 {
53 if (n >= 0)
54 {
55 return n;
56 }
57 else
58 {
59 return -(1 + n);
60 }
61 }
62
calcLevelBounds(const Vec2 & lodBounds,const int levelCount,VkSamplerMipmapMode mipmapFilter)63 UVec2 calcLevelBounds (const Vec2& lodBounds,
64 const int levelCount,
65 VkSamplerMipmapMode mipmapFilter)
66 {
67 DE_ASSERT(lodBounds[0] <= lodBounds[1]);
68 DE_ASSERT(levelCount > 0);
69
70 const float q = (float) (levelCount - 1);
71
72 UVec2 levelBounds;
73
74 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_NEAREST)
75 {
76 if (lodBounds[0] <= 0.5f)
77 {
78 levelBounds[0] = 0;
79 }
80 else if (lodBounds[0] < q + 0.5f)
81 {
82 levelBounds[0] = deCeilFloatToInt32(lodBounds[0] + 0.5f) - 1;
83 }
84 else
85 {
86 levelBounds[0] = deRoundFloatToInt32(q);
87 }
88
89 if (lodBounds[1] < 0.5f)
90 {
91 levelBounds[1] = 0;
92 }
93 else if (lodBounds[1] < q + 0.5f)
94 {
95 levelBounds[1] = deFloorFloatToInt32(lodBounds[1] + 0.5f);
96 }
97 else
98 {
99 levelBounds[1] = deRoundFloatToInt32(q);
100 }
101 }
102 else
103 {
104 for (int ndx = 0; ndx < 2; ++ndx)
105 {
106 if (lodBounds[ndx] >= q)
107 {
108 levelBounds[ndx] = deRoundFloatToInt32(q);
109 }
110 else
111 {
112 levelBounds[ndx] = lodBounds[ndx] < 0.0f ? 0 : deFloorFloatToInt32(lodBounds[ndx]);
113 }
114 }
115 }
116
117 return levelBounds;
118 }
119
calcLevelLodBounds(const Vec2 & lodBounds,int level)120 Vec2 calcLevelLodBounds (const Vec2& lodBounds, int level)
121 {
122 Vec2 levelLodBounds;
123
124 if (lodBounds[0] <= 0.0f)
125 {
126 levelLodBounds[0] = lodBounds[0];
127 }
128 else
129 {
130 levelLodBounds[0] = de::max(lodBounds[0], (float) level);
131 }
132
133 levelLodBounds[1] = de::min(lodBounds[1], (float) level + 1.0f);
134
135 return levelLodBounds;
136 }
137
addUlp(float num,deInt32 ulp)138 float addUlp (float num, deInt32 ulp)
139 {
140 // Note: adding positive ulp always moves float away from zero
141
142 const tcu::Float32 f(num);
143
144 DE_ASSERT(!f.isNaN() && !f.isInf());
145 DE_ASSERT(num > FLT_MIN * (float) ulp || num < FLT_MIN * (float) ulp);
146
147 return tcu::Float32(f.bits() + ulp).asFloat();
148 }
149
wrapTexelGridCoordLinear(IVec3 & baseTexel,IVec3 & texelGridOffset,const int coordBits,const ImgDim dim)150 void wrapTexelGridCoordLinear (IVec3& baseTexel,
151 IVec3& texelGridOffset,
152 const int coordBits,
153 const ImgDim dim)
154 {
155 const int subdivisions = 1 << coordBits;
156
157 int numComp;
158
159 switch (dim)
160 {
161 case IMG_DIM_1D:
162 numComp = 1;
163 break;
164
165 case IMG_DIM_2D:
166 numComp = 2;
167 break;
168
169 case IMG_DIM_CUBE:
170 numComp = 2;
171 break;
172
173 case IMG_DIM_3D:
174 numComp = 3;
175 break;
176
177 default:
178 numComp = 0;
179 break;
180 }
181
182 for (int compNdx = 0; compNdx < numComp; ++compNdx)
183 {
184 texelGridOffset[compNdx] -= subdivisions / (int) 2;
185
186 if (texelGridOffset[compNdx] < 0)
187 {
188 baseTexel [compNdx] -= 1;
189 texelGridOffset[compNdx] += (deInt32) subdivisions;
190 }
191 }
192 }
193
calcTexelBaseOffset(const IVec3 & gridCoord,const int coordBits,IVec3 & baseTexel,IVec3 & texelGridOffset)194 void calcTexelBaseOffset (const IVec3& gridCoord,
195 const int coordBits,
196 IVec3& baseTexel,
197 IVec3& texelGridOffset)
198 {
199 const int subdivisions = (int) 1 << coordBits;
200
201 for (int compNdx = 0; compNdx < 3; ++compNdx)
202 {
203 // \todo [2016-07-22 collinbaker] Do floor division to properly handle negative coords
204 baseTexel[compNdx] = gridCoord[compNdx] / (deInt32) subdivisions;
205 texelGridOffset[compNdx] = gridCoord[compNdx] % (deInt32) subdivisions;
206 }
207 }
208
calcTexelGridCoordRange(const Vec3 & unnormalizedCoordMin,const Vec3 & unnormalizedCoordMax,const int coordBits,IVec3 & gridCoordMin,IVec3 & gridCoordMax)209 void calcTexelGridCoordRange (const Vec3& unnormalizedCoordMin,
210 const Vec3& unnormalizedCoordMax,
211 const int coordBits,
212 IVec3& gridCoordMin,
213 IVec3& gridCoordMax)
214 {
215 const int subdivisions = 1 << coordBits;
216
217 for (int compNdx = 0; compNdx < 3; ++compNdx)
218 {
219 const float comp[2] = {unnormalizedCoordMin[compNdx],
220 unnormalizedCoordMax[compNdx]};
221
222 float fracPart[2];
223 double intPart[2];
224
225 for (int ndx = 0; ndx < 2; ++ndx)
226 {
227 fracPart[ndx] = (float) deModf(comp[ndx], &intPart[ndx]);
228
229 if (comp[ndx] < 0.0f)
230 {
231 intPart [ndx] -= 1.0;
232 fracPart[ndx] += 1.0f;
233 }
234 }
235
236 const deInt32 nearestTexelGridOffsetMin = (deInt32) deFloor(intPart[0]);
237 const deInt32 nearestTexelGridOffsetMax = (deInt32) deFloor(intPart[1]);
238
239 const deInt32 subTexelGridCoordMin = de::max((deInt32) deFloor(fracPart[0] * (float) subdivisions), (deInt32) 0);
240 const deInt32 subTexelGridCoordMax = de::min((deInt32) deCeil (fracPart[1] * (float) subdivisions), (deInt32) (subdivisions - 1));
241
242 gridCoordMin[compNdx] = nearestTexelGridOffsetMin * (deInt32) subdivisions + subTexelGridCoordMin;
243 gridCoordMax[compNdx] = nearestTexelGridOffsetMax * (deInt32) subdivisions + subTexelGridCoordMax;
244 }
245 }
246
calcUnnormalizedCoordRange(const Vec4 & coord,const IVec3 & levelSize,const FloatFormat & internalFormat,Vec3 & unnormalizedCoordMin,Vec3 & unnormalizedCoordMax)247 void calcUnnormalizedCoordRange (const Vec4& coord,
248 const IVec3& levelSize,
249 const FloatFormat& internalFormat,
250 Vec3& unnormalizedCoordMin,
251 Vec3& unnormalizedCoordMax)
252 {
253 for (int compNdx = 0; compNdx < 3; ++compNdx)
254 {
255 const int size = levelSize[compNdx];
256
257 Interval coordInterval = Interval(coord[compNdx]);
258 coordInterval = internalFormat.roundOut(coordInterval, false);
259
260 Interval unnormalizedCoordInterval = coordInterval * Interval((double) size);
261 unnormalizedCoordInterval = internalFormat.roundOut(unnormalizedCoordInterval, false);
262
263 unnormalizedCoordMin[compNdx] = (float)unnormalizedCoordInterval.lo();
264 unnormalizedCoordMax[compNdx] = (float)unnormalizedCoordInterval.hi();
265 }
266 }
267
calcLodBounds(const Vec3 & dPdx,const Vec3 & dPdy,const IVec3 size,const float lodBias,const float lodMin,const float lodMax)268 Vec2 calcLodBounds (const Vec3& dPdx,
269 const Vec3& dPdy,
270 const IVec3 size,
271 const float lodBias,
272 const float lodMin,
273 const float lodMax)
274 {
275 Vec2 lodBounds;
276
277 const Vec3 mx = abs(dPdx) * size.asFloat();
278 const Vec3 my = abs(dPdy) * size.asFloat();
279
280 Vec2 scaleXBounds;
281 Vec2 scaleYBounds;
282
283 scaleXBounds[0] = de::max(de::abs(mx[0]), de::max(de::abs(mx[1]), de::abs(mx[2])));
284 scaleYBounds[0] = de::max(de::abs(my[0]), de::max(de::abs(my[1]), de::abs(my[2])));
285
286 scaleXBounds[1] = de::abs(mx[0]) + de::abs(mx[1]) + de::abs(mx[2]);
287 scaleYBounds[1] = de::abs(my[0]) + de::abs(my[1]) + de::abs(my[2]);
288
289 Vec2 scaleMaxBounds;
290
291 for (int compNdx = 0; compNdx < 2; ++compNdx)
292 {
293 scaleMaxBounds[compNdx] = de::max(scaleXBounds[compNdx], scaleYBounds[compNdx]);
294 }
295
296 for (int ndx = 0; ndx < 2; ++ndx)
297 {
298 lodBounds[ndx] = deFloatLog2(scaleMaxBounds[ndx]);
299 lodBounds[ndx] += lodBias;
300 lodBounds[ndx] = de::clamp(lodBounds[ndx], lodMin, lodMax);
301 }
302
303 return lodBounds;
304 }
305
calcCubemapFaceCoords(const Vec3 & r,const Vec3 & drdx,const Vec3 & drdy,const int faceNdx,Vec2 & coordFace,Vec2 & dPdxFace,Vec2 & dPdyFace)306 void calcCubemapFaceCoords (const Vec3& r,
307 const Vec3& drdx,
308 const Vec3& drdy,
309 const int faceNdx,
310 Vec2& coordFace,
311 Vec2& dPdxFace,
312 Vec2& dPdyFace)
313 {
314 DE_ASSERT(faceNdx >= 0 && faceNdx < 6);
315
316 static const int compMap[6][3] =
317 {
318 {2, 1, 0},
319 {2, 1, 0},
320 {0, 2, 1},
321 {0, 2, 1},
322 {0, 1, 2},
323 {0, 1, 2}
324 };
325
326 static const int signMap[6][3] =
327 {
328 {-1, -1, +1},
329 {+1, -1, -1},
330 {+1, +1, +1},
331 {+1, -1, -1},
332 {+1, -1, +1},
333 {-1, -1, -1}
334 };
335
336 Vec3 coordC;
337 Vec3 dPcdx;
338 Vec3 dPcdy;
339
340 for (int compNdx = 0; compNdx < 3; ++compNdx)
341 {
342 const int mappedComp = compMap[faceNdx][compNdx];
343 const int mappedSign = signMap[faceNdx][compNdx];
344
345 coordC[compNdx] = r [mappedComp] * (float)mappedSign;
346 dPcdx [compNdx] = drdx[mappedComp] * (float)mappedSign;
347 dPcdy [compNdx] = drdy[mappedComp] * (float)mappedSign;
348 }
349
350 DE_ASSERT(coordC[2] != 0.0f);
351 coordC[2] = de::abs(coordC[2]);
352
353 for (int compNdx = 0; compNdx < 2; ++compNdx)
354 {
355 coordFace[compNdx] = 0.5f * coordC[compNdx] / de::abs(coordC[2]) + 0.5f;
356
357 dPdxFace [compNdx] = 0.5f * (de::abs(coordC[2]) * dPcdx[compNdx] - coordC[compNdx] * dPcdx[2]) / (coordC[2] * coordC[2]);
358 dPdyFace [compNdx] = 0.5f * (de::abs(coordC[2]) * dPcdy[compNdx] - coordC[compNdx] * dPcdy[2]) / (coordC[2] * coordC[2]);
359 }
360 }
361
calcCandidateCubemapFaces(const Vec3 & r)362 int calcCandidateCubemapFaces (const Vec3& r)
363 {
364 deUint8 faceBitmap = 0;
365 float rMax = de::abs(r[0]);
366
367 for (int compNdx = 1; compNdx < 3; ++compNdx)
368 {
369 rMax = de::max(rMax, de::abs(r[compNdx]));
370 }
371
372 for (int compNdx = 0; compNdx < 3; ++compNdx)
373 {
374 if (de::abs(r[compNdx]) == rMax)
375 {
376 const int faceNdx = 2 * compNdx + (r[compNdx] < 0.0f ? 1 : 0);
377
378 DE_ASSERT(faceNdx < 6);
379
380 faceBitmap = (deUint8)(faceBitmap | (deUint8) (1U << faceNdx));
381 }
382 }
383
384 DE_ASSERT(faceBitmap != 0U);
385
386 return faceBitmap;
387 }
388
wrapTexelCoord(const deInt32 coord,const int size,const VkSamplerAddressMode wrap)389 deInt32 wrapTexelCoord (const deInt32 coord,
390 const int size,
391 const VkSamplerAddressMode wrap)
392 {
393 deInt32 wrappedCoord = 0;
394
395 switch (wrap)
396 {
397 case VK_SAMPLER_ADDRESS_MODE_REPEAT:
398 wrappedCoord = mod(coord, size);
399 break;
400
401 case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
402 wrappedCoord = (size - 1) - mirror(mod(coord, 2 * size) - size);
403 break;
404
405 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
406 wrappedCoord = de::clamp(coord, 0, (deInt32) size - 1);
407 break;
408
409 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
410 wrappedCoord = de::clamp(coord, -1, (deInt32) size);
411 break;
412
413 case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
414 wrappedCoord = de::clamp(mirror(coord), 0, (deInt32) size - 1);
415 break;
416
417 default:
418 DE_FATAL("Invalid VkSamplerAddressMode");
419 break;
420 }
421
422 return wrappedCoord;
423 }
424
425 namespace
426 {
427
428 // Cube map adjacent faces ordered clockwise from top
429 // \todo [2016-07-07 collinbaker] Verify these are correct
430 static const int adjacentFaces[6][4] =
431 {
432 {3, 5, 2, 4},
433 {3, 4, 2, 5},
434 {4, 0, 5, 1},
435 {5, 0, 4, 1},
436 {3, 0, 2, 1},
437 {3, 1, 2, 0}
438 };
439
440 static const int adjacentEdges[6][4] =
441 {
442 {1, 3, 1, 1},
443 {3, 3, 3, 1},
444 {2, 2, 2, 2},
445 {0, 0, 0, 0},
446 {2, 3, 0, 1},
447 {0, 3, 2, 1}
448 };
449
450 static const int adjacentEdgeDirs[6][4] =
451 {
452 {-1, +1, +1, +1},
453 {+1, +1, -1, +1},
454 {+1, +1, -1, -1},
455 {-1, -1, +1, +1},
456 {+1, +1, +1, +1},
457 {-1, +1, -1, +1}
458 };
459
460 static const int edgeComponent[4] = {0, 1, 0, 1};
461
462 static const int edgeFactors[4][2] =
463 {
464 {0, 0},
465 {1, 0},
466 {0, 1},
467 {0, 0}
468 };
469
470 } // anonymous
471
wrapCubemapEdge(const IVec2 & coord,const IVec2 & size,const int faceNdx,IVec2 & newCoord,int & newFaceNdx)472 void wrapCubemapEdge (const IVec2& coord,
473 const IVec2& size,
474 const int faceNdx,
475 IVec2& newCoord,
476 int& newFaceNdx)
477 {
478 int edgeNdx = -1;
479
480 if (coord[1] < 0)
481 {
482 edgeNdx = 0;
483 }
484 else if (coord[0] > 0)
485 {
486 edgeNdx = 1;
487 }
488 else if (coord[1] > 0)
489 {
490 edgeNdx = 2;
491 }
492 else
493 {
494 edgeNdx = 3;
495 }
496
497 const int adjacentEdgeNdx = adjacentEdges[faceNdx][edgeNdx];
498 const IVec2 edgeFactor = IVec2(edgeFactors[adjacentEdgeNdx][0],
499 edgeFactors[adjacentEdgeNdx][1]);
500 const IVec2 edgeOffset = edgeFactor * (size - IVec2(1));
501
502 if (adjacentEdgeDirs[faceNdx][edgeNdx] > 0)
503 {
504 newCoord[edgeComponent[adjacentEdgeNdx]] = coord[edgeComponent[edgeNdx]];
505 }
506 else
507 {
508 newCoord[edgeComponent[adjacentEdgeNdx]] =
509 size[edgeComponent[edgeNdx]] - coord[edgeComponent[edgeNdx]] - 1;
510 }
511
512 newCoord[1 - edgeComponent[adjacentEdgeNdx]] = 0;
513 newCoord += edgeOffset;
514
515 newFaceNdx = adjacentFaces[faceNdx][edgeNdx];
516 }
517
wrapCubemapCorner(const IVec2 & coord,const IVec2 & size,const int faceNdx,int & adjacentFace1,int & adjacentFace2,IVec2 & cornerCoord0,IVec2 & cornerCoord1,IVec2 & cornerCoord2)518 void wrapCubemapCorner (const IVec2& coord,
519 const IVec2& size,
520 const int faceNdx,
521 int& adjacentFace1,
522 int& adjacentFace2,
523 IVec2& cornerCoord0,
524 IVec2& cornerCoord1,
525 IVec2& cornerCoord2)
526 {
527 int cornerNdx = -1;
528
529 if (coord[0] < 0 && coord[1] < 0)
530 {
531 cornerNdx = 0;
532 }
533 else if (coord[0] > 0 && coord[1] < 0)
534 {
535 cornerNdx = 1;
536 }
537 else if (coord[0] > 0 && coord[1] > 0)
538 {
539 cornerNdx = 2;
540 }
541 else
542 {
543 cornerNdx = 3;
544 }
545
546 const int cornerEdges[2] = {cornerNdx, (int) ((cornerNdx + 3) % 4)};
547
548 int faceCorners[3] = {cornerNdx, 0, 0};
549
550 for (int edgeNdx = 0; edgeNdx < 2; ++edgeNdx)
551 {
552 const int faceEdge = adjacentEdges[faceNdx][cornerEdges[edgeNdx]];
553
554 bool isFlipped = (adjacentEdgeDirs[faceNdx][cornerEdges[edgeNdx]] == -1);
555
556 if ((cornerEdges[edgeNdx] > 1) != (faceEdge > 1))
557 {
558 isFlipped = !isFlipped;
559 }
560
561 if (isFlipped)
562 {
563 faceCorners[edgeNdx + 1] = (faceEdge + 1) % 4;
564 }
565 else
566 {
567 faceCorners[edgeNdx + 1] = faceEdge;
568 }
569 }
570
571 adjacentFace1 = adjacentFaces[faceNdx][cornerEdges[0]];
572 adjacentFace2 = adjacentFaces[faceNdx][cornerEdges[1]];
573
574 IVec2* cornerCoords[3] = {&cornerCoord0, &cornerCoord1, &cornerCoord2};
575
576 for (int ndx = 0; ndx < 3; ++ndx)
577 {
578 IVec2 cornerFactor;
579
580 switch (faceCorners[faceNdx])
581 {
582 case 0:
583 cornerFactor = IVec2(0, 0);
584 break;
585
586 case 1:
587 cornerFactor = IVec2(1, 0);
588 break;
589
590 case 2:
591 cornerFactor = IVec2(1, 1);
592 break;
593
594 case 3:
595 cornerFactor = IVec2(0, 1);
596 break;
597
598 default:
599 break;
600 }
601
602 *cornerCoords[ndx] = cornerFactor * (size - IVec2(1));
603 }
604 }
605
606 namespace
607 {
608
signExtend(deUint64 src,int bits)609 deInt64 signExtend (deUint64 src, int bits)
610 {
611 const deUint64 signBit = 1ull << (bits-1);
612
613 src |= ~((src & signBit) - 1);
614
615 return (deInt64) src;
616 }
617
convertFP16(const void * fp16Ptr,const de::SharedPtr<FloatFormat> & internalFormat,float & resultMin,float & resultMax)618 void convertFP16 (const void* fp16Ptr,
619 const de::SharedPtr<FloatFormat>& internalFormat,
620 float& resultMin,
621 float& resultMax)
622 {
623 const Float16 fp16(*(const deUint16*) fp16Ptr);
624 const Interval fpInterval = internalFormat->roundOut(Interval(fp16.asDouble()), false);
625
626 resultMin = (float) fpInterval.lo();
627 resultMax = (float) fpInterval.hi();
628 }
629
convertNormalizedInt(deInt64 num,int numBits,bool isSigned,const de::SharedPtr<tcu::FloatFormat> & internalFormat,float & resultMin,float & resultMax)630 void convertNormalizedInt (deInt64 num,
631 int numBits,
632 bool isSigned,
633 const de::SharedPtr<tcu::FloatFormat>& internalFormat,
634 float& resultMin,
635 float& resultMax)
636 {
637 DE_ASSERT(numBits > 0);
638
639 const double c = (double) num;
640 deUint64 exp = numBits;
641
642 if (isSigned)
643 --exp;
644
645 const double div = (double) (((deUint64) 1 << exp) - 1);
646 const double value = de::max(c / div, -1.0);
647
648 Interval resultInterval(value - internalFormat->ulp(value), value + internalFormat->ulp(value));
649 resultInterval = internalFormat->roundOut(resultInterval, false);
650
651 resultMin = (float) resultInterval.lo();
652 resultMax = (float) resultInterval.hi();
653 }
654
isPackedType(const TextureFormat::ChannelType type)655 bool isPackedType (const TextureFormat::ChannelType type)
656 {
657 DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 48);
658
659 switch (type)
660 {
661 case TextureFormat::UNORM_BYTE_44:
662 case TextureFormat::UNORM_SHORT_565:
663 case TextureFormat::UNORM_SHORT_555:
664 case TextureFormat::UNORM_SHORT_4444:
665 case TextureFormat::UNORM_SHORT_5551:
666 case TextureFormat::UNORM_SHORT_1555:
667 case TextureFormat::UNORM_INT_101010:
668 case TextureFormat::SNORM_INT_1010102_REV:
669 case TextureFormat::UNORM_INT_1010102_REV:
670 case TextureFormat::SSCALED_INT_1010102_REV:
671 case TextureFormat::USCALED_INT_1010102_REV:
672 return true;
673
674 default:
675 return false;
676 }
677 }
678
getPackInfo(const TextureFormat texFormat,IVec4 & bitSizes,IVec4 & bitOffsets,int & baseTypeBytes)679 void getPackInfo (const TextureFormat texFormat,
680 IVec4& bitSizes,
681 IVec4& bitOffsets,
682 int& baseTypeBytes)
683 {
684 DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 48);
685
686 switch (texFormat.type)
687 {
688 case TextureFormat::UNORM_BYTE_44:
689 bitSizes = IVec4(4, 4, 0, 0);
690 bitOffsets = IVec4(0, 4, 0, 0);
691 baseTypeBytes = 1;
692 break;
693
694 case TextureFormat::UNORM_SHORT_565:
695 bitSizes = IVec4(5, 6, 5, 0);
696 bitOffsets = IVec4(0, 5, 11, 0);
697 baseTypeBytes = 2;
698 break;
699
700 case TextureFormat::UNORM_SHORT_555:
701 bitSizes = IVec4(5, 5, 5, 0);
702 bitOffsets = IVec4(0, 5, 10, 0);
703 baseTypeBytes = 2;
704 break;
705
706 case TextureFormat::UNORM_SHORT_4444:
707 bitSizes = IVec4(4, 4, 4, 4);
708 bitOffsets = IVec4(0, 4, 8, 12);
709 baseTypeBytes = 2;
710 break;
711
712 case TextureFormat::UNORM_SHORT_5551:
713 bitSizes = IVec4(5, 5, 5, 1);
714 bitOffsets = IVec4(0, 5, 10, 15);
715 baseTypeBytes = 2;
716 break;
717
718 case TextureFormat::UNORM_SHORT_1555:
719 bitSizes = IVec4(1, 5, 5, 5);
720 bitOffsets = IVec4(0, 1, 6, 11);
721 baseTypeBytes = 2;
722 break;
723
724 case TextureFormat::UNORM_INT_101010:
725 bitSizes = IVec4(10, 10, 10, 0);
726 bitOffsets = IVec4(0, 10, 20, 0);
727 baseTypeBytes = 4;
728 break;
729
730 case TextureFormat::SNORM_INT_1010102_REV:
731 case TextureFormat::SSCALED_INT_1010102_REV:
732 bitSizes = IVec4(2, 10, 10, 10);
733 bitOffsets = IVec4(0, 2, 12, 22);
734 baseTypeBytes = 4;
735 break;
736
737 case TextureFormat::UNORM_INT_1010102_REV:
738 case TextureFormat::USCALED_INT_1010102_REV:
739 bitSizes = IVec4(2, 10, 10, 10);
740 bitOffsets = IVec4(0, 2, 12, 22);
741 baseTypeBytes = 4;
742 break;
743
744 default:
745 DE_FATAL("Invalid texture channel type");
746 return;
747 }
748 }
749
750 template <typename BaseType>
unpackBits(const BaseType pack,const int bitOffset,const int numBits)751 deUint64 unpackBits (const BaseType pack,
752 const int bitOffset,
753 const int numBits)
754 {
755 DE_ASSERT(bitOffset + numBits <= 8 * (int) sizeof(BaseType));
756
757 const BaseType mask = (BaseType) (((BaseType) 1 << (BaseType) numBits) - (BaseType) 1);
758
759 return mask & (pack >> (BaseType) (8 * (int) sizeof(BaseType) - bitOffset - numBits));
760 }
761
readChannel(const void * ptr,const int byteOffset,const int numBytes)762 deUint64 readChannel (const void* ptr,
763 const int byteOffset,
764 const int numBytes)
765 {
766 const deUint8* cPtr = (const deUint8*) ptr + byteOffset;
767 deUint64 result = 0;
768
769 for (int byteNdx = 0; byteNdx < numBytes; ++byteNdx)
770 {
771 result = (result << 8U) | (deUint64) (cPtr[numBytes - byteNdx - 1]);
772 }
773
774 return result;
775 }
776
convertNormalizedFormat(const void * pixelPtr,TextureFormat texFormat,const std::vector<de::SharedPtr<FloatFormat>> & internalFormat,Vec4 & resultMin,Vec4 & resultMax)777 void convertNormalizedFormat (const void* pixelPtr,
778 TextureFormat texFormat,
779 const std::vector<de::SharedPtr<FloatFormat>>& internalFormat,
780 Vec4& resultMin,
781 Vec4& resultMax)
782 {
783 TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order);
784 const TextureChannelClass chanClass = getTextureChannelClass(texFormat.type);
785
786 DE_ASSERT(getTextureChannelClass(texFormat.type) < 2);
787
788 // Information for non-packed types
789 int chanSize = -1;
790
791 // Information for packed types
792 IVec4 bitOffsets;
793 IVec4 bitSizes;
794 int baseTypeBytes = -1;
795
796 const bool isPacked = isPackedType(texFormat.type);
797
798 if (isPacked)
799 {
800 getPackInfo(texFormat, bitSizes, bitOffsets, baseTypeBytes);
801
802 // Kludge to work around deficiency in framework
803
804 if (texFormat.type == TextureFormat::UNORM_INT_1010102_REV ||
805 texFormat.type == TextureFormat::SNORM_INT_1010102_REV)
806 {
807 for (int ndx = 0; ndx < 2; ++ndx)
808 {
809 std::swap(readSwizzle.components[ndx], readSwizzle.components[3 - ndx]);
810 }
811 }
812
813 DE_ASSERT(baseTypeBytes == 1 || baseTypeBytes == 2 || baseTypeBytes == 4);
814 }
815 else
816 {
817 chanSize = getChannelSize(texFormat.type);
818 }
819
820 const bool isSigned = (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT);
821 const bool isSrgb = isSRGB(texFormat);
822
823 // \todo [2016-08-01 collinbaker] Handle sRGB with correct rounding
824 DE_ASSERT(!isSrgb);
825 DE_UNREF(isSrgb);
826
827 for (int compNdx = 0; compNdx < 4; ++compNdx)
828 {
829 const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
830
831 if (chan == TextureSwizzle::CHANNEL_ZERO)
832 {
833 resultMin[compNdx] = 0.0f;
834 resultMax[compNdx] = 0.0f;
835 }
836 else if (chan == TextureSwizzle::CHANNEL_ONE)
837 {
838 resultMin[compNdx] = 1.0f;
839 resultMax[compNdx] = 1.0f;
840 }
841 else
842 {
843 deUint64 chanUVal = 0;
844 int chanBits = 0;
845
846 if (isPacked)
847 {
848 deUint64 pack = readChannel(pixelPtr, 0, baseTypeBytes);
849 chanBits = bitSizes[chan];
850
851 switch (baseTypeBytes)
852 {
853 case 1:
854 chanUVal = unpackBits<deUint8>((deUint8)pack, bitOffsets[chan], bitSizes[chan]);
855 break;
856
857 case 2:
858 chanUVal = unpackBits<deUint16>((deUint16)pack, bitOffsets[chan], bitSizes[chan]);
859 break;
860
861 case 4:
862 chanUVal = unpackBits<deUint32>((deUint32)pack, bitOffsets[chan], bitSizes[chan]);
863 break;
864
865 default:
866 break;
867 }
868 }
869 else
870 {
871 chanUVal = readChannel(pixelPtr, chan * chanSize, chanSize);
872 chanBits = 8 * chanSize;
873 }
874
875 deInt64 chanVal = 0;
876
877 if (isSigned)
878 {
879 chanVal = signExtend(chanUVal, chanBits);
880 }
881 else
882 {
883 chanVal = (deInt64) chanUVal;
884 }
885
886 convertNormalizedInt(chanVal, chanBits, isSigned, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]);
887
888 // Special handling for components represented as 1 bit. In this case the only possible
889 // converted values are 0.0 and 1.0, even after using roundOut() to account for the min
890 // and max range of the converted value. For 1 bit values the min will always equal max.
891 // To better reflect actual implementations sampling and filtering of converted 1 bit
892 // values we need to modify the min/max range to include at least one ULP of the
893 // internalFormat we're using. So if we're using 8 bit fractional precision for the
894 // conversion instead a 1 bit value of "0" resulting in [0.0 .. 0.0] it will instead
895 // be [0.0 .. 0.00390625], and a value of "1" resulting in [1.0 .. 1.0] will instead
896 // be [0.99609375 .. 1.0]. Later when these values are used for calculating the
897 // reference sampled and filtered values there will be a range that implementations
898 // can fall between. Without this change, even after the reference sampling and filtering
899 // calculations, there will be zero tolerance in the acceptable range since min==max
900 // leaving zero room for rounding errors and arithmetic precision in the implementation.
901 if (chanBits == 1)
902 {
903 if (resultMin[compNdx] == 1.0f)
904 resultMin[compNdx] -= float(internalFormat[compNdx]->ulp(1.0));
905 if (resultMax[compNdx] == 0.0f)
906 resultMax[compNdx] += float(internalFormat[compNdx]->ulp(0.0));
907 }
908 }
909 }
910 }
911
convertFloatFormat(const void * pixelPtr,TextureFormat texFormat,const std::vector<de::SharedPtr<FloatFormat>> & internalFormat,Vec4 & resultMin,Vec4 & resultMax)912 void convertFloatFormat (const void* pixelPtr,
913 TextureFormat texFormat,
914 const std::vector<de::SharedPtr<FloatFormat>>& internalFormat,
915 Vec4& resultMin,
916 Vec4& resultMax)
917 {
918 DE_ASSERT(getTextureChannelClass(texFormat.type) == TEXTURECHANNELCLASS_FLOATING_POINT);
919
920 const TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order);
921
922 for (int compNdx = 0; compNdx < 4; ++compNdx)
923 {
924 const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
925
926 if (chan == TextureSwizzle::CHANNEL_ZERO)
927 {
928 resultMin[compNdx] = 0.0f;
929 resultMax[compNdx] = 0.0f;
930 }
931 else if (chan == TextureSwizzle::CHANNEL_ONE)
932 {
933 resultMin[compNdx] = 1.0f;
934 resultMax[compNdx] = 1.0f;
935 }
936 else if (texFormat.type == TextureFormat::FLOAT)
937 {
938 resultMin[compNdx] = resultMax[compNdx] = *((const float*)pixelPtr + chan);
939 }
940 else if (texFormat.type == TextureFormat::HALF_FLOAT)
941 {
942 convertFP16((const deUint16*) pixelPtr + chan, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]);
943 }
944 else
945 {
946 DE_FATAL("Unsupported floating point format");
947 }
948 }
949 }
950
951 } // anonymous
952
convertFormat(const void * pixelPtr,TextureFormat texFormat,const std::vector<de::SharedPtr<FloatFormat>> & internalFormat,Vec4 & resultMin,Vec4 & resultMax)953 void convertFormat (const void* pixelPtr,
954 TextureFormat texFormat,
955 const std::vector<de::SharedPtr<FloatFormat>>& internalFormat,
956 Vec4& resultMin,
957 Vec4& resultMax)
958 {
959 const TextureChannelClass chanClass = getTextureChannelClass(texFormat.type);
960
961 // \todo [2016-08-01 collinbaker] Handle float and shared exponent formats
962 if (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || chanClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
963 {
964 convertNormalizedFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
965 }
966 else if (chanClass == TEXTURECHANNELCLASS_FLOATING_POINT)
967 {
968 convertFloatFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
969 }
970 else
971 {
972 DE_FATAL("Unimplemented");
973 }
974 }
975
976 } // util
977 } // texture
978 } // vkt
979