1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2016 Google Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief GPU image sample verification
22 *//*--------------------------------------------------------------------*/
24 #include "vktSampleVerifierUtil.hpp"
27 #include "tcuDefs.hpp"
28 #include "tcuFloat.hpp"
29 #include "tcuFloatFormat.hpp"
30 #include "tcuInterval.hpp"
31 #include "tcuTexture.hpp"
32 #include "tcuTextureUtil.hpp"
44 deInt32 mod (const deInt32 a, const deInt32 n)
46 const deInt32 result = a % n;
48 return (result < 0) ? result + n : result;
51 deInt32 mirror (const deInt32 n)
63 UVec2 calcLevelBounds (const Vec2& lodBounds,
65 VkSamplerMipmapMode mipmapFilter)
67 DE_ASSERT(lodBounds[0] <= lodBounds[1]);
68 DE_ASSERT(levelCount > 0);
70 const float q = (float) (levelCount - 1);
74 if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_NEAREST)
76 if (lodBounds[0] <= 0.5f)
80 else if (lodBounds[0] < q + 0.5f)
82 levelBounds[0] = deCeilFloatToInt32(lodBounds[0] + 0.5f) - 1;
86 levelBounds[0] = deRoundFloatToInt32(q);
89 if (lodBounds[1] < 0.5f)
93 else if (lodBounds[1] < q + 0.5f)
95 levelBounds[1] = deFloorFloatToInt32(lodBounds[1] + 0.5f);
99 levelBounds[1] = deRoundFloatToInt32(q);
104 for (int ndx = 0; ndx < 2; ++ndx)
106 if (lodBounds[ndx] >= q)
108 levelBounds[ndx] = deRoundFloatToInt32(q);
112 levelBounds[ndx] = lodBounds[ndx] < 0.0f ? 0 : deFloorFloatToInt32(lodBounds[ndx]);
120 Vec2 calcLevelLodBounds (const Vec2& lodBounds, int level)
124 if (lodBounds[0] <= 0.0f)
126 levelLodBounds[0] = lodBounds[0];
130 levelLodBounds[0] = de::max(lodBounds[0], (float) level);
133 levelLodBounds[1] = de::min(lodBounds[1], (float) level + 1.0f);
135 return levelLodBounds;
138 float addUlp (float num, deInt32 ulp)
140 // Note: adding positive ulp always moves float away from zero
142 const tcu::Float32 f(num);
144 DE_ASSERT(!f.isNaN() && !f.isInf());
145 DE_ASSERT(num > FLT_MIN * (float) ulp || num < FLT_MIN * (float) ulp);
147 return tcu::Float32(f.bits() + ulp).asFloat();
150 void wrapTexelGridCoordLinear (IVec3& baseTexel,
151 IVec3& texelGridOffset,
155 const int subdivisions = 1 << coordBits;
182 for (int compNdx = 0; compNdx < numComp; ++compNdx)
184 texelGridOffset[compNdx] -= subdivisions / (int) 2;
186 if (texelGridOffset[compNdx] < 0)
188 baseTexel [compNdx] -= 1;
189 texelGridOffset[compNdx] += (deInt32) subdivisions;
194 void calcTexelBaseOffset (const IVec3& gridCoord,
197 IVec3& texelGridOffset)
199 const int subdivisions = (int) 1 << coordBits;
201 for (int compNdx = 0; compNdx < 3; ++compNdx)
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;
209 void calcTexelGridCoordRange (const Vec3& unnormalizedCoordMin,
210 const Vec3& unnormalizedCoordMax,
215 const int subdivisions = 1 << coordBits;
217 for (int compNdx = 0; compNdx < 3; ++compNdx)
219 const float comp[2] = {unnormalizedCoordMin[compNdx],
220 unnormalizedCoordMax[compNdx]};
225 for (int ndx = 0; ndx < 2; ++ndx)
227 fracPart[ndx] = (float) deModf(comp[ndx], &intPart[ndx]);
229 if (comp[ndx] < 0.0f)
231 intPart [ndx] -= 1.0;
232 fracPart[ndx] += 1.0f;
236 const deInt32 nearestTexelGridOffsetMin = (deInt32) deFloor(intPart[0]);
237 const deInt32 nearestTexelGridOffsetMax = (deInt32) deFloor(intPart[1]);
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));
242 gridCoordMin[compNdx] = nearestTexelGridOffsetMin * (deInt32) subdivisions + subTexelGridCoordMin;
243 gridCoordMax[compNdx] = nearestTexelGridOffsetMax * (deInt32) subdivisions + subTexelGridCoordMax;
247 void calcUnnormalizedCoordRange (const Vec4& coord,
248 const IVec3& levelSize,
249 const FloatFormat& internalFormat,
250 Vec3& unnormalizedCoordMin,
251 Vec3& unnormalizedCoordMax)
253 for (int compNdx = 0; compNdx < 3; ++compNdx)
255 const int size = levelSize[compNdx];
257 Interval coordInterval = Interval(coord[compNdx]);
258 coordInterval = internalFormat.roundOut(coordInterval, false);
260 Interval unnormalizedCoordInterval = coordInterval * Interval((double) size);
261 unnormalizedCoordInterval = internalFormat.roundOut(unnormalizedCoordInterval, false);
263 unnormalizedCoordMin[compNdx] = (float)unnormalizedCoordInterval.lo();
264 unnormalizedCoordMax[compNdx] = (float)unnormalizedCoordInterval.hi();
268 Vec2 calcLodBounds (const Vec3& dPdx,
277 const Vec3 mx = abs(dPdx) * size.asFloat();
278 const Vec3 my = abs(dPdy) * size.asFloat();
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])));
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]);
291 for (int compNdx = 0; compNdx < 2; ++compNdx)
293 scaleMaxBounds[compNdx] = de::max(scaleXBounds[compNdx], scaleYBounds[compNdx]);
296 for (int ndx = 0; ndx < 2; ++ndx)
298 lodBounds[ndx] = deFloatLog2(scaleMaxBounds[ndx]);
299 lodBounds[ndx] += lodBias;
300 lodBounds[ndx] = de::clamp(lodBounds[ndx], lodMin, lodMax);
306 void calcCubemapFaceCoords (const Vec3& r,
314 DE_ASSERT(faceNdx >= 0 && faceNdx < 6);
316 static const int compMap[6][3] =
326 static const int signMap[6][3] =
340 for (int compNdx = 0; compNdx < 3; ++compNdx)
342 const int mappedComp = compMap[faceNdx][compNdx];
343 const int mappedSign = signMap[faceNdx][compNdx];
345 coordC[compNdx] = r [mappedComp] * (float)mappedSign;
346 dPcdx [compNdx] = drdx[mappedComp] * (float)mappedSign;
347 dPcdy [compNdx] = drdy[mappedComp] * (float)mappedSign;
350 DE_ASSERT(coordC[2] != 0.0f);
351 coordC[2] = de::abs(coordC[2]);
353 for (int compNdx = 0; compNdx < 2; ++compNdx)
355 coordFace[compNdx] = 0.5f * coordC[compNdx] / de::abs(coordC[2]) + 0.5f;
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]);
362 int calcCandidateCubemapFaces (const Vec3& r)
364 deUint8 faceBitmap = 0;
365 float rMax = de::abs(r[0]);
367 for (int compNdx = 1; compNdx < 3; ++compNdx)
369 rMax = de::max(rMax, de::abs(r[compNdx]));
372 for (int compNdx = 0; compNdx < 3; ++compNdx)
374 if (de::abs(r[compNdx]) == rMax)
376 const int faceNdx = 2 * compNdx + (r[compNdx] < 0.0f ? 1 : 0);
378 DE_ASSERT(faceNdx < 6);
380 faceBitmap = (deUint8)(faceBitmap | (deUint8) (1U << faceNdx));
384 DE_ASSERT(faceBitmap != 0U);
389 deInt32 wrapTexelCoord (const deInt32 coord,
391 const VkSamplerAddressMode wrap)
393 deInt32 wrappedCoord = 0;
397 case VK_SAMPLER_ADDRESS_MODE_REPEAT:
398 wrappedCoord = mod(coord, size);
401 case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
402 wrappedCoord = (size - 1) - mirror(mod(coord, 2 * size) - size);
405 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
406 wrappedCoord = de::clamp(coord, 0, (deInt32) size - 1);
409 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
410 wrappedCoord = de::clamp(coord, -1, (deInt32) size);
413 case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
414 wrappedCoord = de::clamp(mirror(coord), 0, (deInt32) size - 1);
418 DE_FATAL("Invalid VkSamplerAddressMode");
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] =
440 static const int adjacentEdges[6][4] =
450 static const int adjacentEdgeDirs[6][4] =
460 static const int edgeComponent[4] = {0, 1, 0, 1};
462 static const int edgeFactors[4][2] =
472 void wrapCubemapEdge (const IVec2& coord,
484 else if (coord[0] > 0)
488 else if (coord[1] > 0)
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));
502 if (adjacentEdgeDirs[faceNdx][edgeNdx] > 0)
504 newCoord[edgeComponent[adjacentEdgeNdx]] = coord[edgeComponent[edgeNdx]];
508 newCoord[edgeComponent[adjacentEdgeNdx]] =
509 size[edgeComponent[edgeNdx]] - coord[edgeComponent[edgeNdx]] - 1;
512 newCoord[1 - edgeComponent[adjacentEdgeNdx]] = 0;
513 newCoord += edgeOffset;
515 newFaceNdx = adjacentFaces[faceNdx][edgeNdx];
518 void wrapCubemapCorner (const IVec2& coord,
529 if (coord[0] < 0 && coord[1] < 0)
533 else if (coord[0] > 0 && coord[1] < 0)
537 else if (coord[0] > 0 && coord[1] > 0)
546 const int cornerEdges[2] = {cornerNdx, (int) ((cornerNdx + 3) % 4)};
548 int faceCorners[3] = {cornerNdx, 0, 0};
550 for (int edgeNdx = 0; edgeNdx < 2; ++edgeNdx)
552 const int faceEdge = adjacentEdges[faceNdx][cornerEdges[edgeNdx]];
554 bool isFlipped = (adjacentEdgeDirs[faceNdx][cornerEdges[edgeNdx]] == -1);
556 if ((cornerEdges[edgeNdx] > 1) != (faceEdge > 1))
558 isFlipped = !isFlipped;
563 faceCorners[edgeNdx + 1] = (faceEdge + 1) % 4;
567 faceCorners[edgeNdx + 1] = faceEdge;
571 adjacentFace1 = adjacentFaces[faceNdx][cornerEdges[0]];
572 adjacentFace2 = adjacentFaces[faceNdx][cornerEdges[1]];
574 IVec2* cornerCoords[3] = {&cornerCoord0, &cornerCoord1, &cornerCoord2};
576 for (int ndx = 0; ndx < 3; ++ndx)
580 switch (faceCorners[faceNdx])
583 cornerFactor = IVec2(0, 0);
587 cornerFactor = IVec2(1, 0);
591 cornerFactor = IVec2(1, 1);
595 cornerFactor = IVec2(0, 1);
602 *cornerCoords[ndx] = cornerFactor * (size - IVec2(1));
609 deInt64 signExtend (deUint64 src, int bits)
611 const deUint64 signBit = 1ull << (bits-1);
613 src |= ~((src & signBit) - 1);
615 return (deInt64) src;
618 void convertFP16 (const void* fp16Ptr,
619 FloatFormat internalFormat,
623 const Float16 fp16(*(const deUint16*) fp16Ptr);
624 const Interval fpInterval = internalFormat.roundOut(Interval(fp16.asDouble()), false);
626 resultMin = (float) fpInterval.lo();
627 resultMax = (float) fpInterval.hi();
630 void convertNormalizedInt (deInt64 num,
633 FloatFormat internalFormat,
637 DE_ASSERT(numBits > 0);
639 const double c = (double) num;
640 deUint64 exp = numBits;
645 const double div = (double) (((deUint64) 1 << exp) - 1);
647 Interval resultInterval(de::max(c / div, -1.0));
648 resultInterval = internalFormat.roundOut(resultInterval, false);
650 resultMin = (float) resultInterval.lo();
651 resultMax = (float) resultInterval.hi();
654 bool isPackedType (const TextureFormat::ChannelType type)
656 DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 42);
660 case TextureFormat::UNORM_BYTE_44:
661 case TextureFormat::UNORM_SHORT_565:
662 case TextureFormat::UNORM_SHORT_555:
663 case TextureFormat::UNORM_SHORT_4444:
664 case TextureFormat::UNORM_SHORT_5551:
665 case TextureFormat::UNORM_SHORT_1555:
666 case TextureFormat::UNORM_INT_101010:
667 case TextureFormat::SNORM_INT_1010102_REV:
668 case TextureFormat::UNORM_INT_1010102_REV:
676 void getPackInfo (const TextureFormat texFormat,
681 DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 42);
683 switch (texFormat.type)
685 case TextureFormat::UNORM_BYTE_44:
686 bitSizes = IVec4(4, 4, 0, 0);
687 bitOffsets = IVec4(0, 4, 0, 0);
691 case TextureFormat::UNORM_SHORT_565:
692 bitSizes = IVec4(5, 6, 5, 0);
693 bitOffsets = IVec4(0, 5, 11, 0);
697 case TextureFormat::UNORM_SHORT_555:
698 bitSizes = IVec4(5, 5, 5, 0);
699 bitOffsets = IVec4(0, 5, 10, 0);
703 case TextureFormat::UNORM_SHORT_4444:
704 bitSizes = IVec4(4, 4, 4, 4);
705 bitOffsets = IVec4(0, 4, 8, 12);
709 case TextureFormat::UNORM_SHORT_5551:
710 bitSizes = IVec4(5, 5, 5, 1);
711 bitOffsets = IVec4(0, 5, 10, 15);
715 case TextureFormat::UNORM_SHORT_1555:
716 bitSizes = IVec4(1, 5, 5, 5);
717 bitOffsets = IVec4(0, 1, 6, 11);
721 case TextureFormat::UNORM_INT_101010:
722 bitSizes = IVec4(10, 10, 10, 0);
723 bitOffsets = IVec4(0, 10, 20, 0);
727 case TextureFormat::SNORM_INT_1010102_REV:
728 bitSizes = IVec4(2, 10, 10, 10);
729 bitOffsets = IVec4(0, 2, 12, 22);
733 case TextureFormat::UNORM_INT_1010102_REV:
734 bitSizes = IVec4(2, 10, 10, 10);
735 bitOffsets = IVec4(0, 2, 12, 22);
740 DE_FATAL("Invalid texture channel type");
745 template <typename BaseType>
746 deUint64 unpackBits (const BaseType pack,
750 DE_ASSERT(bitOffset + numBits <= 8 * (int) sizeof(BaseType));
752 const BaseType mask = (BaseType) (((BaseType) 1 << (BaseType) numBits) - (BaseType) 1);
754 return mask & (pack >> (BaseType) (8 * (int) sizeof(BaseType) - bitOffset - numBits));
757 deUint64 readChannel (const void* ptr,
758 const int byteOffset,
761 const deUint8* cPtr = (const deUint8*) ptr + byteOffset;
764 for (int byteNdx = 0; byteNdx < numBytes; ++byteNdx)
766 result = (result << 8U) | (deUint64) (cPtr[numBytes - byteNdx - 1]);
772 void convertNormalizedFormat (const void* pixelPtr,
773 TextureFormat texFormat,
774 const std::vector<FloatFormat>& internalFormat,
778 TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order);
779 const TextureChannelClass chanClass = getTextureChannelClass(texFormat.type);
781 DE_ASSERT(getTextureChannelClass(texFormat.type) < 2);
783 // Information for non-packed types
786 // Information for packed types
789 int baseTypeBytes = -1;
791 const bool isPacked = isPackedType(texFormat.type);
795 getPackInfo(texFormat, bitSizes, bitOffsets, baseTypeBytes);
797 // Kludge to work around deficiency in framework
799 if (texFormat.type == TextureFormat::UNORM_INT_1010102_REV ||
800 texFormat.type == TextureFormat::SNORM_INT_1010102_REV)
802 for (int ndx = 0; ndx < 2; ++ndx)
804 std::swap(readSwizzle.components[ndx], readSwizzle.components[3 - ndx]);
808 DE_ASSERT(baseTypeBytes == 1 || baseTypeBytes == 2 || baseTypeBytes == 4);
812 chanSize = getChannelSize(texFormat.type);
815 const bool isSigned = (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT);
816 const bool isSrgb = isSRGB(texFormat);
818 // \todo [2016-08-01 collinbaker] Handle sRGB with correct rounding
822 for (int compNdx = 0; compNdx < 4; ++compNdx)
824 const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
826 if (chan == TextureSwizzle::CHANNEL_ZERO)
828 resultMin[compNdx] = 0.0f;
829 resultMax[compNdx] = 0.0f;
831 else if (chan == TextureSwizzle::CHANNEL_ONE)
833 resultMin[compNdx] = 1.0f;
834 resultMax[compNdx] = 1.0f;
838 deUint64 chanUVal = 0;
843 deUint64 pack = readChannel(pixelPtr, 0, baseTypeBytes);
844 chanBits = bitSizes[chan];
846 switch (baseTypeBytes)
849 chanUVal = unpackBits<deUint8>((deUint8)pack, bitOffsets[chan], bitSizes[chan]);
853 chanUVal = unpackBits<deUint16>((deUint16)pack, bitOffsets[chan], bitSizes[chan]);
857 chanUVal = unpackBits<deUint32>((deUint32)pack, bitOffsets[chan], bitSizes[chan]);
866 chanUVal = readChannel(pixelPtr, chan * chanSize, chanSize);
867 chanBits = 8 * chanSize;
874 chanVal = signExtend(chanUVal, chanBits);
878 chanVal = (deInt64) chanUVal;
881 convertNormalizedInt(chanVal, chanBits, isSigned, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]);
883 // Special handling for components represented as 1 bit. In this case the only possible
884 // converted values are 0.0 and 1.0, even after using roundOut() to account for the min
885 // and max range of the converted value. For 1 bit values the min will always equal max.
886 // To better reflect actual implementations sampling and filtering of converted 1 bit
887 // values we need to modify the min/max range to include at least one ULP of the
888 // internalFormat we're using. So if we're using 8 bit fractional precision for the
889 // conversion instead a 1 bit value of "0" resulting in [0.0 .. 0.0] it will instead
890 // be [0.0 .. 0.00390625], and a value of "1" resulting in [1.0 .. 1.0] will instead
891 // be [0.99609375 .. 1.0]. Later when these values are used for calculating the
892 // reference sampled and filtered values there will be a range that implementations
893 // can fall between. Without this change, even after the reference sampling and filtering
894 // calculations, there will be zero tolerance in the acceptable range since min==max
895 // leaving zero room for rounding errors and arithmetic precision in the implementation.
898 if (resultMin[compNdx] == 1.0f)
899 resultMin[compNdx] -= float(internalFormat[compNdx].ulp(1.0));
900 if (resultMax[compNdx] == 0.0f)
901 resultMax[compNdx] += float(internalFormat[compNdx].ulp(0.0));
907 void convertFloatFormat (const void* pixelPtr,
908 TextureFormat texFormat,
909 const std::vector<FloatFormat>& internalFormat,
913 DE_ASSERT(getTextureChannelClass(texFormat.type) == TEXTURECHANNELCLASS_FLOATING_POINT);
915 const TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order);
917 for (int compNdx = 0; compNdx < 4; ++compNdx)
919 const TextureSwizzle::Channel chan = readSwizzle.components[compNdx];
921 if (chan == TextureSwizzle::CHANNEL_ZERO)
923 resultMin[compNdx] = 0.0f;
924 resultMax[compNdx] = 0.0f;
926 else if (chan == TextureSwizzle::CHANNEL_ONE)
928 resultMin[compNdx] = 1.0f;
929 resultMax[compNdx] = 1.0f;
931 else if (texFormat.type == TextureFormat::FLOAT)
933 resultMin[compNdx] = resultMax[compNdx] = *((const float*)pixelPtr + chan);
935 else if (texFormat.type == TextureFormat::HALF_FLOAT)
937 convertFP16((const deUint16*) pixelPtr + chan, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]);
941 DE_FATAL("Unsupported floating point format");
948 void convertFormat (const void* pixelPtr,
949 TextureFormat texFormat,
950 const std::vector<FloatFormat>& internalFormat,
954 const TextureChannelClass chanClass = getTextureChannelClass(texFormat.type);
956 // \todo [2016-08-01 collinbaker] Handle float and shared exponent formats
957 if (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || chanClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
959 convertNormalizedFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
961 else if (chanClass == TEXTURECHANNELCLASS_FLOATING_POINT)
963 convertFloatFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax);
967 DE_FATAL("Unimplemented");