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