| /*------------------------------------------------------------------------- |
| * 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 = 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, |
| FloatFormat 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, |
| FloatFormat 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); |
| |
| Interval resultInterval(de::max(c / div, -1.0)); |
| 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 == 38); |
| |
| 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: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| void getPackInfo (const TextureFormat texFormat, |
| IVec4& bitSizes, |
| IVec4& bitOffsets, |
| int& baseTypeBytes) |
| { |
| DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 38); |
| |
| 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: |
| bitSizes = IVec4(2, 10, 10, 10); |
| bitOffsets = IVec4(0, 2, 12, 22); |
| baseTypeBytes = 4; |
| break; |
| |
| case TextureFormat::UNORM_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 <typename BaseType> |
| 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, |
| FloatFormat 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>((deUint8)pack, bitOffsets[chan], bitSizes[chan]); |
| break; |
| |
| case 2: |
| chanUVal = unpackBits<deUint16>((deUint16)pack, bitOffsets[chan], bitSizes[chan]); |
| break; |
| |
| case 4: |
| chanUVal = unpackBits<deUint32>((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, resultMin[compNdx], resultMax[compNdx]); |
| } |
| } |
| } |
| |
| void convertFloatFormat (const void* pixelPtr, |
| TextureFormat texFormat, |
| FloatFormat 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, resultMin[compNdx], resultMax[compNdx]); |
| } |
| else |
| { |
| DE_FATAL("Unsupported floating point format"); |
| } |
| } |
| } |
| |
| } // anonymous |
| |
| void convertFormat (const void* pixelPtr, |
| TextureFormat texFormat, |
| FloatFormat 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 |