/*------------------------------------------------------------------------- * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2016 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief GPU image sample verification *//*--------------------------------------------------------------------*/ #include "vktSampleVerifierUtil.hpp" #include "deMath.h" #include "tcuDefs.hpp" #include "tcuFloat.hpp" #include "tcuFloatFormat.hpp" #include "tcuInterval.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" namespace vkt { namespace texture { namespace util { using namespace tcu; using namespace vk; deInt32 mod (const deInt32 a, const deInt32 n) { const deInt32 result = a % n; return (result < 0) ? result + n : result; } deInt32 mirror (const deInt32 n) { if (n >= 0) { return n; } else { return -(1 + n); } } UVec2 calcLevelBounds (const Vec2& lodBounds, const int levelCount, VkSamplerMipmapMode mipmapFilter) { DE_ASSERT(lodBounds[0] <= lodBounds[1]); DE_ASSERT(levelCount > 0); const float q = (float) (levelCount - 1); UVec2 levelBounds; if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_NEAREST) { if (lodBounds[0] <= 0.5f) { levelBounds[0] = 0; } else if (lodBounds[0] < q + 0.5f) { levelBounds[0] = deCeilFloatToInt32(lodBounds[0] + 0.5f) - 1; } else { levelBounds[0] = deRoundFloatToInt32(q); } if (lodBounds[1] < 0.5f) { levelBounds[1] = 0; } else if (lodBounds[1] < q + 0.5f) { levelBounds[1] = deFloorFloatToInt32(lodBounds[1] + 0.5f); } else { levelBounds[1] = deRoundFloatToInt32(q); } } else { for (int ndx = 0; ndx < 2; ++ndx) { if (lodBounds[ndx] >= q) { levelBounds[ndx] = deRoundFloatToInt32(q); } else { levelBounds[ndx] = lodBounds[ndx] < 0.0f ? 0 : deFloorFloatToInt32(lodBounds[ndx]); } } } return levelBounds; } Vec2 calcLevelLodBounds (const Vec2& lodBounds, int level) { Vec2 levelLodBounds; if (lodBounds[0] <= 0.0f) { levelLodBounds[0] = lodBounds[0]; } else { levelLodBounds[0] = de::max(lodBounds[0], (float) level); } levelLodBounds[1] = de::min(lodBounds[1], (float) level + 1.0f); return levelLodBounds; } float addUlp (float num, deInt32 ulp) { // Note: adding positive ulp always moves float away from zero const tcu::Float32 f(num); DE_ASSERT(!f.isNaN() && !f.isInf()); DE_ASSERT(num > FLT_MIN * (float) ulp || num < FLT_MIN * (float) ulp); return tcu::Float32(f.bits() + ulp).asFloat(); } void wrapTexelGridCoordLinear (IVec3& baseTexel, IVec3& texelGridOffset, const int coordBits, const ImgDim dim) { const int subdivisions = 1 << coordBits; int numComp; switch (dim) { case IMG_DIM_1D: numComp = 1; break; case IMG_DIM_2D: numComp = 2; break; case IMG_DIM_CUBE: numComp = 2; break; case IMG_DIM_3D: numComp = 3; break; default: numComp = 0; break; } for (int compNdx = 0; compNdx < numComp; ++compNdx) { texelGridOffset[compNdx] -= subdivisions / (int) 2; if (texelGridOffset[compNdx] < 0) { baseTexel [compNdx] -= 1; texelGridOffset[compNdx] += (deInt32) subdivisions; } } } void calcTexelBaseOffset (const IVec3& gridCoord, const int coordBits, IVec3& baseTexel, IVec3& texelGridOffset) { const int subdivisions = (int) 1 << coordBits; for (int compNdx = 0; compNdx < 3; ++compNdx) { // \todo [2016-07-22 collinbaker] Do floor division to properly handle negative coords baseTexel[compNdx] = gridCoord[compNdx] / (deInt32) subdivisions; texelGridOffset[compNdx] = gridCoord[compNdx] % (deInt32) subdivisions; } } void calcTexelGridCoordRange (const Vec3& unnormalizedCoordMin, const Vec3& unnormalizedCoordMax, const int coordBits, IVec3& gridCoordMin, IVec3& gridCoordMax) { const int subdivisions = 1 << coordBits; for (int compNdx = 0; compNdx < 3; ++compNdx) { const float comp[2] = {unnormalizedCoordMin[compNdx], unnormalizedCoordMax[compNdx]}; float fracPart[2]; double intPart[2]; for (int ndx = 0; ndx < 2; ++ndx) { fracPart[ndx] = (float) deModf(comp[ndx], &intPart[ndx]); if (comp[ndx] < 0.0f) { intPart [ndx] -= 1.0; fracPart[ndx] += 1.0f; } } const deInt32 nearestTexelGridOffsetMin = (deInt32) deFloor(intPart[0]); const deInt32 nearestTexelGridOffsetMax = (deInt32) deFloor(intPart[1]); const deInt32 subTexelGridCoordMin = de::max((deInt32) deFloor(fracPart[0] * (float) subdivisions), (deInt32) 0); const deInt32 subTexelGridCoordMax = de::min((deInt32) deCeil (fracPart[1] * (float) subdivisions), (deInt32) (subdivisions - 1)); gridCoordMin[compNdx] = nearestTexelGridOffsetMin * (deInt32) subdivisions + subTexelGridCoordMin; gridCoordMax[compNdx] = nearestTexelGridOffsetMax * (deInt32) subdivisions + subTexelGridCoordMax; } } void calcUnnormalizedCoordRange (const Vec4& coord, const IVec3& levelSize, const FloatFormat& internalFormat, Vec3& unnormalizedCoordMin, Vec3& unnormalizedCoordMax) { for (int compNdx = 0; compNdx < 3; ++compNdx) { const int size = levelSize[compNdx]; Interval coordInterval = Interval(coord[compNdx]); coordInterval = internalFormat.roundOut(coordInterval, false); Interval unnormalizedCoordInterval = coordInterval * Interval((double) size); unnormalizedCoordInterval = internalFormat.roundOut(unnormalizedCoordInterval, false); unnormalizedCoordMin[compNdx] = (float)unnormalizedCoordInterval.lo(); unnormalizedCoordMax[compNdx] = (float)unnormalizedCoordInterval.hi(); } } Vec2 calcLodBounds (const Vec3& dPdx, const Vec3& dPdy, const IVec3 size, const float lodBias, const float lodMin, const float lodMax) { Vec2 lodBounds; const Vec3 mx = abs(dPdx) * size.asFloat(); const Vec3 my = abs(dPdy) * size.asFloat(); Vec2 scaleXBounds; Vec2 scaleYBounds; scaleXBounds[0] = de::max(de::abs(mx[0]), de::max(de::abs(mx[1]), de::abs(mx[2]))); scaleYBounds[0] = de::max(de::abs(my[0]), de::max(de::abs(my[1]), de::abs(my[2]))); scaleXBounds[1] = de::abs(mx[0]) + de::abs(mx[1]) + de::abs(mx[2]); scaleYBounds[1] = de::abs(my[0]) + de::abs(my[1]) + de::abs(my[2]); Vec2 scaleMaxBounds; for (int compNdx = 0; compNdx < 2; ++compNdx) { scaleMaxBounds[compNdx] = de::max(scaleXBounds[compNdx], scaleYBounds[compNdx]); } for (int ndx = 0; ndx < 2; ++ndx) { lodBounds[ndx] = deFloatLog2(scaleMaxBounds[ndx]); lodBounds[ndx] += lodBias; lodBounds[ndx] = de::clamp(lodBounds[ndx], lodMin, lodMax); } return lodBounds; } void calcCubemapFaceCoords (const Vec3& r, const Vec3& drdx, const Vec3& drdy, const int faceNdx, Vec2& coordFace, Vec2& dPdxFace, Vec2& dPdyFace) { DE_ASSERT(faceNdx >= 0 && faceNdx < 6); static const int compMap[6][3] = { {2, 1, 0}, {2, 1, 0}, {0, 2, 1}, {0, 2, 1}, {0, 1, 2}, {0, 1, 2} }; static const int signMap[6][3] = { {-1, -1, +1}, {+1, -1, -1}, {+1, +1, +1}, {+1, -1, -1}, {+1, -1, +1}, {-1, -1, -1} }; Vec3 coordC; Vec3 dPcdx; Vec3 dPcdy; for (int compNdx = 0; compNdx < 3; ++compNdx) { const int mappedComp = compMap[faceNdx][compNdx]; const int mappedSign = signMap[faceNdx][compNdx]; coordC[compNdx] = r [mappedComp] * (float)mappedSign; dPcdx [compNdx] = drdx[mappedComp] * (float)mappedSign; dPcdy [compNdx] = drdy[mappedComp] * (float)mappedSign; } DE_ASSERT(coordC[2] != 0.0f); coordC[2] = de::abs(coordC[2]); for (int compNdx = 0; compNdx < 2; ++compNdx) { coordFace[compNdx] = 0.5f * coordC[compNdx] / de::abs(coordC[2]) + 0.5f; dPdxFace [compNdx] = 0.5f * (de::abs(coordC[2]) * dPcdx[compNdx] - coordC[compNdx] * dPcdx[2]) / (coordC[2] * coordC[2]); dPdyFace [compNdx] = 0.5f * (de::abs(coordC[2]) * dPcdy[compNdx] - coordC[compNdx] * dPcdy[2]) / (coordC[2] * coordC[2]); } } int calcCandidateCubemapFaces (const Vec3& r) { deUint8 faceBitmap = 0; float rMax = de::abs(r[0]); for (int compNdx = 1; compNdx < 3; ++compNdx) { rMax = de::max(rMax, de::abs(r[compNdx])); } for (int compNdx = 0; compNdx < 3; ++compNdx) { if (de::abs(r[compNdx]) == rMax) { const int faceNdx = 2 * compNdx + (r[compNdx] < 0.0f ? 1 : 0); DE_ASSERT(faceNdx < 6); faceBitmap = (deUint8)(faceBitmap | (deUint8) (1U << faceNdx)); } } DE_ASSERT(faceBitmap != 0U); return faceBitmap; } deInt32 wrapTexelCoord (const deInt32 coord, const int size, const VkSamplerAddressMode wrap) { deInt32 wrappedCoord = 0; switch (wrap) { case VK_SAMPLER_ADDRESS_MODE_REPEAT: wrappedCoord = mod(coord, size); break; case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: wrappedCoord = (size - 1) - mirror(mod(coord, 2 * size) - size); break; case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: wrappedCoord = de::clamp(coord, 0, (deInt32) size - 1); break; case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: wrappedCoord = de::clamp(coord, -1, (deInt32) size); break; case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: wrappedCoord = de::clamp(mirror(coord), 0, (deInt32) size - 1); break; default: DE_FATAL("Invalid VkSamplerAddressMode"); break; } return wrappedCoord; } namespace { // Cube map adjacent faces ordered clockwise from top // \todo [2016-07-07 collinbaker] Verify these are correct static const int adjacentFaces[6][4] = { {3, 5, 2, 4}, {3, 4, 2, 5}, {4, 0, 5, 1}, {5, 0, 4, 1}, {3, 0, 2, 1}, {3, 1, 2, 0} }; static const int adjacentEdges[6][4] = { {1, 3, 1, 1}, {3, 3, 3, 1}, {2, 2, 2, 2}, {0, 0, 0, 0}, {2, 3, 0, 1}, {0, 3, 2, 1} }; static const int adjacentEdgeDirs[6][4] = { {-1, +1, +1, +1}, {+1, +1, -1, +1}, {+1, +1, -1, -1}, {-1, -1, +1, +1}, {+1, +1, +1, +1}, {-1, +1, -1, +1} }; static const int edgeComponent[4] = {0, 1, 0, 1}; static const int edgeFactors[4][2] = { {0, 0}, {1, 0}, {0, 1}, {0, 0} }; } // anonymous void wrapCubemapEdge (const IVec2& coord, const IVec2& size, const int faceNdx, IVec2& newCoord, int& newFaceNdx) { int edgeNdx = -1; if (coord[1] < 0) { edgeNdx = 0; } else if (coord[0] > 0) { edgeNdx = 1; } else if (coord[1] > 0) { edgeNdx = 2; } else { edgeNdx = 3; } const int adjacentEdgeNdx = adjacentEdges[faceNdx][edgeNdx]; const IVec2 edgeFactor = IVec2(edgeFactors[adjacentEdgeNdx][0], edgeFactors[adjacentEdgeNdx][1]); const IVec2 edgeOffset = edgeFactor * (size - IVec2(1)); if (adjacentEdgeDirs[faceNdx][edgeNdx] > 0) { newCoord[edgeComponent[adjacentEdgeNdx]] = coord[edgeComponent[edgeNdx]]; } else { newCoord[edgeComponent[adjacentEdgeNdx]] = size[edgeComponent[edgeNdx]] - coord[edgeComponent[edgeNdx]] - 1; } newCoord[1 - edgeComponent[adjacentEdgeNdx]] = 0; newCoord += edgeOffset; newFaceNdx = adjacentFaces[faceNdx][edgeNdx]; } void wrapCubemapCorner (const IVec2& coord, const IVec2& size, const int faceNdx, int& adjacentFace1, int& adjacentFace2, IVec2& cornerCoord0, IVec2& cornerCoord1, IVec2& cornerCoord2) { int cornerNdx = -1; if (coord[0] < 0 && coord[1] < 0) { cornerNdx = 0; } else if (coord[0] > 0 && coord[1] < 0) { cornerNdx = 1; } else if (coord[0] > 0 && coord[1] > 0) { cornerNdx = 2; } else { cornerNdx = 3; } const int cornerEdges[2] = {cornerNdx, (int) ((cornerNdx + 3) % 4)}; int faceCorners[3] = {cornerNdx, 0, 0}; for (int edgeNdx = 0; edgeNdx < 2; ++edgeNdx) { const int faceEdge = adjacentEdges[faceNdx][cornerEdges[edgeNdx]]; bool isFlipped = (adjacentEdgeDirs[faceNdx][cornerEdges[edgeNdx]] == -1); if ((cornerEdges[edgeNdx] > 1) != (faceEdge > 1)) { isFlipped = !isFlipped; } if (isFlipped) { faceCorners[edgeNdx + 1] = (faceEdge + 1) % 4; } else { faceCorners[edgeNdx + 1] = faceEdge; } } adjacentFace1 = adjacentFaces[faceNdx][cornerEdges[0]]; adjacentFace2 = adjacentFaces[faceNdx][cornerEdges[1]]; IVec2* cornerCoords[3] = {&cornerCoord0, &cornerCoord1, &cornerCoord2}; for (int ndx = 0; ndx < 3; ++ndx) { IVec2 cornerFactor; switch (faceCorners[faceNdx]) { case 0: cornerFactor = IVec2(0, 0); break; case 1: cornerFactor = IVec2(1, 0); break; case 2: cornerFactor = IVec2(1, 1); break; case 3: cornerFactor = IVec2(0, 1); break; default: break; } *cornerCoords[ndx] = cornerFactor * (size - IVec2(1)); } } namespace { deInt64 signExtend (deUint64 src, int bits) { const deUint64 signBit = 1ull << (bits-1); src |= ~((src & signBit) - 1); return (deInt64) src; } void convertFP16 (const void* fp16Ptr, const de::SharedPtr& internalFormat, float& resultMin, float& resultMax) { const Float16 fp16(*(const deUint16*) fp16Ptr); const Interval fpInterval = internalFormat->roundOut(Interval(fp16.asDouble()), false); resultMin = (float) fpInterval.lo(); resultMax = (float) fpInterval.hi(); } void convertNormalizedInt (deInt64 num, int numBits, bool isSigned, const de::SharedPtr& internalFormat, float& resultMin, float& resultMax) { DE_ASSERT(numBits > 0); const double c = (double) num; deUint64 exp = numBits; if (isSigned) --exp; const double div = (double) (((deUint64) 1 << exp) - 1); const double value = de::max(c / div, -1.0); Interval resultInterval(value - internalFormat->ulp(value), value + internalFormat->ulp(value)); resultInterval = internalFormat->roundOut(resultInterval, false); resultMin = (float) resultInterval.lo(); resultMax = (float) resultInterval.hi(); } bool isPackedType (const TextureFormat::ChannelType type) { DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 48); switch (type) { case TextureFormat::UNORM_BYTE_44: case TextureFormat::UNORM_SHORT_565: case TextureFormat::UNORM_SHORT_555: case TextureFormat::UNORM_SHORT_4444: case TextureFormat::UNORM_SHORT_5551: case TextureFormat::UNORM_SHORT_1555: case TextureFormat::UNORM_INT_101010: case TextureFormat::SNORM_INT_1010102_REV: case TextureFormat::UNORM_INT_1010102_REV: case TextureFormat::SSCALED_INT_1010102_REV: case TextureFormat::USCALED_INT_1010102_REV: return true; default: return false; } } void getPackInfo (const TextureFormat texFormat, IVec4& bitSizes, IVec4& bitOffsets, int& baseTypeBytes) { DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 48); switch (texFormat.type) { case TextureFormat::UNORM_BYTE_44: bitSizes = IVec4(4, 4, 0, 0); bitOffsets = IVec4(0, 4, 0, 0); baseTypeBytes = 1; break; case TextureFormat::UNORM_SHORT_565: bitSizes = IVec4(5, 6, 5, 0); bitOffsets = IVec4(0, 5, 11, 0); baseTypeBytes = 2; break; case TextureFormat::UNORM_SHORT_555: bitSizes = IVec4(5, 5, 5, 0); bitOffsets = IVec4(0, 5, 10, 0); baseTypeBytes = 2; break; case TextureFormat::UNORM_SHORT_4444: bitSizes = IVec4(4, 4, 4, 4); bitOffsets = IVec4(0, 4, 8, 12); baseTypeBytes = 2; break; case TextureFormat::UNORM_SHORT_5551: bitSizes = IVec4(5, 5, 5, 1); bitOffsets = IVec4(0, 5, 10, 15); baseTypeBytes = 2; break; case TextureFormat::UNORM_SHORT_1555: bitSizes = IVec4(1, 5, 5, 5); bitOffsets = IVec4(0, 1, 6, 11); baseTypeBytes = 2; break; case TextureFormat::UNORM_INT_101010: bitSizes = IVec4(10, 10, 10, 0); bitOffsets = IVec4(0, 10, 20, 0); baseTypeBytes = 4; break; case TextureFormat::SNORM_INT_1010102_REV: case TextureFormat::SSCALED_INT_1010102_REV: bitSizes = IVec4(2, 10, 10, 10); bitOffsets = IVec4(0, 2, 12, 22); baseTypeBytes = 4; break; case TextureFormat::UNORM_INT_1010102_REV: case TextureFormat::USCALED_INT_1010102_REV: bitSizes = IVec4(2, 10, 10, 10); bitOffsets = IVec4(0, 2, 12, 22); baseTypeBytes = 4; break; default: DE_FATAL("Invalid texture channel type"); return; } } template deUint64 unpackBits (const BaseType pack, const int bitOffset, const int numBits) { DE_ASSERT(bitOffset + numBits <= 8 * (int) sizeof(BaseType)); const BaseType mask = (BaseType) (((BaseType) 1 << (BaseType) numBits) - (BaseType) 1); return mask & (pack >> (BaseType) (8 * (int) sizeof(BaseType) - bitOffset - numBits)); } deUint64 readChannel (const void* ptr, const int byteOffset, const int numBytes) { const deUint8* cPtr = (const deUint8*) ptr + byteOffset; deUint64 result = 0; for (int byteNdx = 0; byteNdx < numBytes; ++byteNdx) { result = (result << 8U) | (deUint64) (cPtr[numBytes - byteNdx - 1]); } return result; } void convertNormalizedFormat (const void* pixelPtr, TextureFormat texFormat, const std::vector>& internalFormat, Vec4& resultMin, Vec4& resultMax) { TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order); const TextureChannelClass chanClass = getTextureChannelClass(texFormat.type); DE_ASSERT(getTextureChannelClass(texFormat.type) < 2); // Information for non-packed types int chanSize = -1; // Information for packed types IVec4 bitOffsets; IVec4 bitSizes; int baseTypeBytes = -1; const bool isPacked = isPackedType(texFormat.type); if (isPacked) { getPackInfo(texFormat, bitSizes, bitOffsets, baseTypeBytes); // Kludge to work around deficiency in framework if (texFormat.type == TextureFormat::UNORM_INT_1010102_REV || texFormat.type == TextureFormat::SNORM_INT_1010102_REV) { for (int ndx = 0; ndx < 2; ++ndx) { std::swap(readSwizzle.components[ndx], readSwizzle.components[3 - ndx]); } } DE_ASSERT(baseTypeBytes == 1 || baseTypeBytes == 2 || baseTypeBytes == 4); } else { chanSize = getChannelSize(texFormat.type); } const bool isSigned = (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT); const bool isSrgb = isSRGB(texFormat); // \todo [2016-08-01 collinbaker] Handle sRGB with correct rounding DE_ASSERT(!isSrgb); DE_UNREF(isSrgb); for (int compNdx = 0; compNdx < 4; ++compNdx) { const TextureSwizzle::Channel chan = readSwizzle.components[compNdx]; if (chan == TextureSwizzle::CHANNEL_ZERO) { resultMin[compNdx] = 0.0f; resultMax[compNdx] = 0.0f; } else if (chan == TextureSwizzle::CHANNEL_ONE) { resultMin[compNdx] = 1.0f; resultMax[compNdx] = 1.0f; } else { deUint64 chanUVal = 0; int chanBits = 0; if (isPacked) { deUint64 pack = readChannel(pixelPtr, 0, baseTypeBytes); chanBits = bitSizes[chan]; switch (baseTypeBytes) { case 1: chanUVal = unpackBits((deUint8)pack, bitOffsets[chan], bitSizes[chan]); break; case 2: chanUVal = unpackBits((deUint16)pack, bitOffsets[chan], bitSizes[chan]); break; case 4: chanUVal = unpackBits((deUint32)pack, bitOffsets[chan], bitSizes[chan]); break; default: break; } } else { chanUVal = readChannel(pixelPtr, chan * chanSize, chanSize); chanBits = 8 * chanSize; } deInt64 chanVal = 0; if (isSigned) { chanVal = signExtend(chanUVal, chanBits); } else { chanVal = (deInt64) chanUVal; } convertNormalizedInt(chanVal, chanBits, isSigned, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]); // Special handling for components represented as 1 bit. In this case the only possible // converted values are 0.0 and 1.0, even after using roundOut() to account for the min // and max range of the converted value. For 1 bit values the min will always equal max. // To better reflect actual implementations sampling and filtering of converted 1 bit // values we need to modify the min/max range to include at least one ULP of the // internalFormat we're using. So if we're using 8 bit fractional precision for the // conversion instead a 1 bit value of "0" resulting in [0.0 .. 0.0] it will instead // be [0.0 .. 0.00390625], and a value of "1" resulting in [1.0 .. 1.0] will instead // be [0.99609375 .. 1.0]. Later when these values are used for calculating the // reference sampled and filtered values there will be a range that implementations // can fall between. Without this change, even after the reference sampling and filtering // calculations, there will be zero tolerance in the acceptable range since min==max // leaving zero room for rounding errors and arithmetic precision in the implementation. if (chanBits == 1) { if (resultMin[compNdx] == 1.0f) resultMin[compNdx] -= float(internalFormat[compNdx]->ulp(1.0)); if (resultMax[compNdx] == 0.0f) resultMax[compNdx] += float(internalFormat[compNdx]->ulp(0.0)); } } } } void convertFloatFormat (const void* pixelPtr, TextureFormat texFormat, const std::vector>& internalFormat, Vec4& resultMin, Vec4& resultMax) { DE_ASSERT(getTextureChannelClass(texFormat.type) == TEXTURECHANNELCLASS_FLOATING_POINT); const TextureSwizzle readSwizzle = getChannelReadSwizzle(texFormat.order); for (int compNdx = 0; compNdx < 4; ++compNdx) { const TextureSwizzle::Channel chan = readSwizzle.components[compNdx]; if (chan == TextureSwizzle::CHANNEL_ZERO) { resultMin[compNdx] = 0.0f; resultMax[compNdx] = 0.0f; } else if (chan == TextureSwizzle::CHANNEL_ONE) { resultMin[compNdx] = 1.0f; resultMax[compNdx] = 1.0f; } else if (texFormat.type == TextureFormat::FLOAT) { resultMin[compNdx] = resultMax[compNdx] = *((const float*)pixelPtr + chan); } else if (texFormat.type == TextureFormat::HALF_FLOAT) { convertFP16((const deUint16*) pixelPtr + chan, internalFormat[compNdx], resultMin[compNdx], resultMax[compNdx]); } else { DE_FATAL("Unsupported floating point format"); } } } } // anonymous void convertFormat (const void* pixelPtr, TextureFormat texFormat, const std::vector>& internalFormat, Vec4& resultMin, Vec4& resultMax) { const TextureChannelClass chanClass = getTextureChannelClass(texFormat.type); // \todo [2016-08-01 collinbaker] Handle float and shared exponent formats if (chanClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || chanClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) { convertNormalizedFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax); } else if (chanClass == TEXTURECHANNELCLASS_FLOATING_POINT) { convertFloatFormat(pixelPtr, texFormat, internalFormat, resultMin, resultMax); } else { DE_FATAL("Unimplemented"); } } } // util } // texture } // vkt