xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/texture/vktSampleVerifierUtil.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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