/*------------------------------------------------------------------------- * 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 "vktSampleVerifier.hpp" #include "vktSampleVerifierUtil.hpp" #include "deMath.h" #include "tcuFloat.hpp" #include "tcuTextureUtil.hpp" #include "vkImageUtil.hpp" #include #include namespace vkt { namespace texture { using namespace vk; using namespace tcu; using namespace util; namespace { int calcUnnormalizedDim (const ImgDim dim) { if (dim == IMG_DIM_1D) { return 1; } else if (dim == IMG_DIM_2D || dim == IMG_DIM_CUBE) { return 2; } else { return 3; } } } // anonymous SampleVerifier::SampleVerifier (const ImageViewParameters& imParams, const SamplerParameters& samplerParams, const SampleLookupSettings& sampleLookupSettings, int coordBits, int mipmapBits, const std::vector>& conversionPrecision, const std::vector>& filteringPrecision, const std::vector& levels) : m_imParams (imParams) , m_samplerParams (samplerParams) , m_sampleLookupSettings (sampleLookupSettings) , m_coordBits (coordBits) , m_mipmapBits (mipmapBits) , m_conversionPrecision (conversionPrecision) , m_filteringPrecision (filteringPrecision) , m_unnormalizedDim (calcUnnormalizedDim(imParams.dim)) , m_levels (levels) { } bool SampleVerifier::coordOutOfRange (const IVec3& coord, int compNdx, int level) const { DE_ASSERT(compNdx >= 0 && compNdx < 3); return coord[compNdx] < 0 || coord[compNdx] >= m_levels[level].getSize()[compNdx]; } void SampleVerifier::fetchTexelWrapped (const IVec3& coord, int layer, int level, Vec4& resultMin, Vec4& resultMax) const { const void* pixelPtr = DE_NULL; if (m_imParams.dim == IMG_DIM_1D) { pixelPtr = m_levels[level].getPixelPtr(coord[0], layer, 0); } else if (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE) { pixelPtr = m_levels[level].getPixelPtr(coord[0], coord[1], layer); } else { pixelPtr = m_levels[level].getPixelPtr(coord[0], coord[1], coord[2]); } convertFormat(pixelPtr, mapVkFormat(m_imParams.format), m_conversionPrecision, resultMin, resultMax); #if defined(DE_DEBUG) // Make sure tcuTexture agrees const tcu::ConstPixelBufferAccess& levelAccess = m_levels[level]; const tcu::Vec4 refPix = (m_imParams.dim == IMG_DIM_1D) ? levelAccess.getPixel(coord[0], layer, 0) : (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE) ? levelAccess.getPixel(coord[0], coord[1], layer) : levelAccess.getPixel(coord[0], coord[1], coord[2]); for (int c = 0; c < 4; c++) DE_ASSERT(de::inRange(refPix[c], resultMin[c], resultMax[c])); #endif } void SampleVerifier::fetchTexel (const IVec3& coordIn, int layer, int level, VkFilter filter, Vec4& resultMin, Vec4& resultMax) const { IVec3 coord = coordIn; VkSamplerAddressMode wrappingModes[] = { m_samplerParams.wrappingModeU, m_samplerParams.wrappingModeV, m_samplerParams.wrappingModeW }; const bool isSrgb = isSrgbFormat(m_imParams.format); // Wrapping operations if (m_imParams.dim == IMG_DIM_CUBE && filter == VK_FILTER_LINEAR) { // If the image is a cubemap and we are using linear filtering, we do edge or corner wrapping const int arrayLayer = layer / 6; int arrayFace = layer % 6; if (coordOutOfRange(coord, 0, level) != coordOutOfRange(coord, 1, level)) { // Wrap around edge IVec2 newCoord(0); int newFace = 0; wrapCubemapEdge(coord.swizzle(0, 1), m_levels[level].getSize().swizzle(0, 1), arrayFace, newCoord, newFace); coord.xy() = newCoord; layer = arrayLayer * 6 + newFace; } else if (coordOutOfRange(coord, 0, level) && coordOutOfRange(coord, 1, level)) { // Wrap corner int faces[3] = {arrayFace, 0, 0}; IVec2 cornerCoords[3]; wrapCubemapCorner(coord.swizzle(0, 1), m_levels[level].getSize().swizzle(0, 1), arrayFace, faces[1], faces[2], cornerCoords[0], cornerCoords[1], cornerCoords[2]); // \todo [2016-08-01 collinbaker] Call into fetchTexelWrapped instead Vec4 cornerTexels[3]; for (int ndx = 0; ndx < 3; ++ndx) { int cornerLayer = faces[ndx] + arrayLayer * 6; if (isSrgb) { cornerTexels[ndx] += sRGBToLinear(m_levels[level].getPixel(cornerCoords[ndx][0], cornerCoords[ndx][1], cornerLayer)); } else { cornerTexels[ndx] += m_levels[level].getPixel(cornerCoords[ndx][0], cornerCoords[ndx][1], cornerLayer); } } for (int compNdx = 0; compNdx < 4; ++compNdx) { float compMin = cornerTexels[0][compNdx]; float compMax = cornerTexels[0][compNdx]; for (int ndx = 1; ndx < 3; ++ndx) { const float comp = cornerTexels[ndx][compNdx]; compMin = de::min(comp, compMin); compMax = de::max(comp, compMax); } resultMin[compNdx] = compMin; resultMax[compNdx] = compMax; } return; } else { // If no wrapping is necessary, just do nothing } } else { // Otherwise, we do normal wrapping if (m_imParams.dim == IMG_DIM_CUBE) { wrappingModes[0] = wrappingModes[1] = wrappingModes[2] = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; } for (int compNdx = 0; compNdx < 3; ++compNdx) { const int size = m_levels[level].getSize()[compNdx]; coord[compNdx] = wrapTexelCoord(coord[compNdx], size, wrappingModes[compNdx]); } } if (coordOutOfRange(coord, 0, level) || coordOutOfRange(coord, 1, level) || coordOutOfRange(coord, 2, level)) { // If after wrapping coordinates are still out of range, perform texel replacement switch (m_samplerParams.borderColor) { case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK: { resultMin = Vec4(0.0f, 0.0f, 0.0f, 0.0f); resultMax = Vec4(0.0f, 0.0f, 0.0f, 0.0f); return; } case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK: { resultMin = Vec4(0.0f, 0.0f, 0.0f, 1.0f); resultMax = Vec4(0.0f, 0.0f, 0.0f, 1.0f); return; } case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE: { resultMin = Vec4(1.0f, 1.0f, 1.0f, 1.0f); resultMax = Vec4(1.0f, 1.0f, 1.0f, 1.0f); return; } default: { // \\ [2016-07-07 collinbaker] Handle // VK_BORDER_COLOR_INT_* borders DE_FATAL("Not implemented"); break; } } } else { // Otherwise, actually fetch a texel fetchTexelWrapped(coord, layer, level, resultMin, resultMax); } } void SampleVerifier::getFilteredSample1D (const IVec3& texelBase, float weight, int layer, int level, Vec4& resultMin, Vec4& resultMax) const { Vec4 texelsMin[2]; Vec4 texelsMax[2]; for (int i = 0; i < 2; ++i) { fetchTexel(texelBase + IVec3(i, 0, 0), layer, level, VK_FILTER_LINEAR, texelsMin[i], texelsMax[i]); } for (int compNdx = 0; compNdx < 4; ++compNdx) { Interval resultInterval(0.0); for (int i = 0; i < 2; ++i) { const Interval weightInterval = m_filteringPrecision[compNdx]->roundOut(Interval(i == 0 ? 1.0f - weight : weight), false); const Interval texelInterval (false, texelsMin[i][compNdx], texelsMax[i][compNdx]); resultInterval = m_filteringPrecision[compNdx]->roundOut(resultInterval + weightInterval * texelInterval, false); } resultMin[compNdx] = (float)resultInterval.lo(); resultMax[compNdx] = (float)resultInterval.hi(); } } void SampleVerifier::getFilteredSample2D (const IVec3& texelBase, const Vec2& weights, int layer, int level, Vec4& resultMin, Vec4& resultMax) const { Vec4 texelsMin[4]; Vec4 texelsMax[4]; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { fetchTexel(texelBase + IVec3(i, j, 0), layer, level, VK_FILTER_LINEAR, texelsMin[2 * j + i], texelsMax[2 * j + i]); } } for (int compNdx = 0; compNdx < 4; ++compNdx) { Interval resultInterval(0.0); for (int i = 0; i < 2; ++i) { const Interval iWeightInterval = m_filteringPrecision[compNdx]->roundOut(Interval(i == 0 ? 1.0f - weights[1] : weights[1]), false); for (int j = 0; j < 2; ++j) { const Interval jWeightInterval = m_filteringPrecision[compNdx]->roundOut(iWeightInterval * Interval(j == 0 ? 1.0f - weights[0] : weights[0]), false); const Interval texelInterval(false, texelsMin[2 * i + j][compNdx], texelsMax[2 * i + j][compNdx]); resultInterval = m_filteringPrecision[compNdx]->roundOut(resultInterval + jWeightInterval * texelInterval, false); } } resultMin[compNdx] = (float)resultInterval.lo(); resultMax[compNdx] = (float)resultInterval.hi(); } } void SampleVerifier::getFilteredSample3D (const IVec3& texelBase, const Vec3& weights, int layer, int level, Vec4& resultMin, Vec4& resultMax) const { Vec4 texelsMin[8]; Vec4 texelsMax[8]; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { for (int k = 0; k < 2; ++k) { fetchTexel(texelBase + IVec3(i, j, k), layer, level, VK_FILTER_LINEAR, texelsMin[4 * k + 2 * j + i], texelsMax[4 * k + 2 * j + i]); } } } for (int compNdx = 0; compNdx < 4; ++compNdx) { Interval resultInterval(0.0); for (int i = 0; i < 2; ++i) { const Interval iWeightInterval = m_filteringPrecision[compNdx]->roundOut(Interval(i == 0 ? 1.0f - weights[2] : weights[2]), false); for (int j = 0; j < 2; ++j) { const Interval jWeightInterval = m_filteringPrecision[compNdx]->roundOut(iWeightInterval * Interval(j == 0 ? 1.0f - weights[1] : weights[1]), false); for (int k = 0; k < 2; ++k) { const Interval kWeightInterval = m_filteringPrecision[compNdx]->roundOut(jWeightInterval * Interval(k == 0 ? 1.0f - weights[0] : weights[0]), false); const Interval texelInterval(false, texelsMin[4 * i + 2 * j + k][compNdx], texelsMax[4 * i + 2 * j + k][compNdx]); resultInterval = m_filteringPrecision[compNdx]->roundOut(resultInterval + kWeightInterval * texelInterval, false); } } } resultMin[compNdx] = (float)resultInterval.lo(); resultMax[compNdx] = (float)resultInterval.hi(); } } void SampleVerifier::getFilteredSample (const IVec3& texelBase, const Vec3& weights, int layer, int level, Vec4& resultMin, Vec4& resultMax) const { DE_ASSERT(layer < m_imParams.arrayLayers); DE_ASSERT(level < m_imParams.levels); if (m_imParams.dim == IMG_DIM_1D) { getFilteredSample1D(texelBase, weights.x(), layer, level, resultMin, resultMax); } else if (m_imParams.dim == IMG_DIM_2D || m_imParams.dim == IMG_DIM_CUBE) { getFilteredSample2D(texelBase, weights.swizzle(0, 1), layer, level, resultMin, resultMax); } else { getFilteredSample3D(texelBase, weights, layer, level, resultMin, resultMax); } } void SampleVerifier::getMipmapStepBounds (const Vec2& lodFracBounds, deInt32& stepMin, deInt32& stepMax) const { DE_ASSERT(m_mipmapBits < 32); const int mipmapSteps = ((int)1) << m_mipmapBits; stepMin = deFloorFloatToInt32(lodFracBounds[0] * (float)mipmapSteps); stepMax = deCeilFloatToInt32 (lodFracBounds[1] * (float)mipmapSteps); stepMin = de::max(stepMin, (deInt32)0); stepMax = de::min(stepMax, (deInt32)mipmapSteps); } bool SampleVerifier::verifySampleFiltered (const Vec4& result, const IVec3& baseTexelHiIn, const IVec3& baseTexelLoIn, const IVec3& texelGridOffsetHiIn, const IVec3& texelGridOffsetLoIn, int layer, int levelHi, const Vec2& lodFracBounds, VkFilter filter, VkSamplerMipmapMode mipmapFilter, std::ostream& report) const { DE_ASSERT(layer < m_imParams.arrayLayers); DE_ASSERT(levelHi < m_imParams.levels); const int coordSteps = 1 << m_coordBits; const int lodSteps = 1 << m_mipmapBits; const int levelLo = (levelHi < m_imParams.levels - 1) ? levelHi + 1 : levelHi; IVec3 baseTexelHi = baseTexelHiIn; IVec3 baseTexelLo = baseTexelLoIn; IVec3 texelGridOffsetHi = texelGridOffsetHiIn; IVec3 texelGridOffsetLo = texelGridOffsetLoIn; deInt32 lodStepsMin = 0; deInt32 lodStepsMax = 0; getMipmapStepBounds(lodFracBounds, lodStepsMin, lodStepsMax); report << "Testing at base texel " << baseTexelHi << ", " << baseTexelLo << " offset " << texelGridOffsetHi << ", " << texelGridOffsetLo << "\n"; Vec4 idealSampleHiMin; Vec4 idealSampleHiMax; Vec4 idealSampleLoMin; Vec4 idealSampleLoMax; // Get ideal samples at steps at each mipmap level if (filter == VK_FILTER_LINEAR) { // Adjust texel grid coordinates for linear filtering wrapTexelGridCoordLinear(baseTexelHi, texelGridOffsetHi, m_coordBits, m_imParams.dim); if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR) { wrapTexelGridCoordLinear(baseTexelLo, texelGridOffsetLo, m_coordBits, m_imParams.dim); } const Vec3 roundedWeightsHi = texelGridOffsetHi.asFloat() / (float)coordSteps; const Vec3 roundedWeightsLo = texelGridOffsetLo.asFloat() / (float)coordSteps; report << "Computed weights: " << roundedWeightsHi << ", " << roundedWeightsLo << "\n"; getFilteredSample(baseTexelHi, roundedWeightsHi, layer, levelHi, idealSampleHiMin, idealSampleHiMax); report << "Ideal hi sample: " << idealSampleHiMin << " through " << idealSampleHiMax << "\n"; if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR) { getFilteredSample(baseTexelLo, roundedWeightsLo, layer, levelLo, idealSampleLoMin, idealSampleLoMax); report << "Ideal lo sample: " << idealSampleLoMin << " through " << idealSampleLoMax << "\n"; } } else { fetchTexel(baseTexelHi, layer, levelHi, VK_FILTER_NEAREST, idealSampleHiMin, idealSampleHiMax); report << "Ideal hi sample: " << idealSampleHiMin << " through " << idealSampleHiMax << "\n"; if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR) { fetchTexel(baseTexelLo, layer, levelLo, VK_FILTER_NEAREST, idealSampleLoMin, idealSampleLoMax); report << "Ideal lo sample: " << idealSampleLoMin << " through " << idealSampleLoMax << "\n"; } } // Test ideal samples based on mipmap filtering mode if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR) { for (deInt32 lodStep = lodStepsMin; lodStep <= lodStepsMax; ++lodStep) { float weight = (float)lodStep / (float)lodSteps; report << "Testing at mipmap weight " << weight << "\n"; Vec4 idealSampleMin; Vec4 idealSampleMax; for (int compNdx = 0; compNdx < 4; ++compNdx) { const Interval idealSampleLo(false, idealSampleLoMin[compNdx], idealSampleLoMax[compNdx]); const Interval idealSampleHi(false, idealSampleHiMin[compNdx], idealSampleHiMax[compNdx]); const Interval idealSample = m_filteringPrecision[compNdx]->roundOut(Interval(weight) * idealSampleLo + Interval(1.0f - weight) * idealSampleHi, false); idealSampleMin[compNdx] = (float)idealSample.lo(); idealSampleMax[compNdx] = (float)idealSample.hi(); } report << "Ideal sample: " << idealSampleMin << " through " << idealSampleMax << "\n"; if (isInRange(result, idealSampleMin, idealSampleMax)) { return true; } else { report << "Failed comparison\n"; } } } else { if (isInRange(result, idealSampleHiMin, idealSampleHiMax)) { return true; } else { report << "Failed comparison\n"; } } return false; } bool SampleVerifier::verifySampleTexelGridCoords (const SampleArguments& args, const Vec4& result, const IVec3& gridCoordHi, const IVec3& gridCoordLo, const Vec2& lodBounds, int level, VkSamplerMipmapMode mipmapFilter, std::ostream& report) const { const int layer = m_imParams.isArrayed ? (int)deRoundEven(args.layer) : 0U; const IVec3 gridCoord[2] = {gridCoordHi, gridCoordLo}; IVec3 baseTexel[2]; IVec3 texelGridOffset[2]; for (int levelNdx = 0; levelNdx < 2; ++levelNdx) { calcTexelBaseOffset(gridCoord[levelNdx], m_coordBits, baseTexel[levelNdx], texelGridOffset[levelNdx]); } const bool canBeMinified = lodBounds[1] > 0.0f; const bool canBeMagnified = lodBounds[0] <= 0.0f; if (canBeMagnified) { report << "Trying magnification...\n"; if (m_samplerParams.magFilter == VK_FILTER_NEAREST) { report << "Testing against nearest texel at " << baseTexel[0] << "\n"; Vec4 idealMin; Vec4 idealMax; fetchTexel(baseTexel[0], layer, level, VK_FILTER_NEAREST, idealMin, idealMax); if (isInRange(result, idealMin, idealMax)) { return true; } else { report << "Failed against " << idealMin << " through " << idealMax << "\n"; } } else { if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer, level, Vec2(0.0f, 0.0f), VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, report)) return true; } } if (canBeMinified) { report << "Trying minification...\n"; if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR) { const Vec2 lodFracBounds = lodBounds - Vec2((float)level); if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer, level, lodFracBounds, m_samplerParams.minFilter, VK_SAMPLER_MIPMAP_MODE_LINEAR, report)) return true; } else if (m_samplerParams.minFilter == VK_FILTER_LINEAR) { if (verifySampleFiltered(result, baseTexel[0], baseTexel[1], texelGridOffset[0], texelGridOffset[1], layer, level, Vec2(0.0f, 0.0f), VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, report)) return true; } else { report << "Testing against nearest texel at " << baseTexel[0] << "\n"; Vec4 idealMin; Vec4 idealMax; fetchTexel(baseTexel[0], layer, level, VK_FILTER_NEAREST, idealMin, idealMax); if (isInRange(result, idealMin, idealMax)) { return true; } else { report << "Failed against " << idealMin << " through " << idealMax << "\n"; } } } return false; } bool SampleVerifier::verifySampleMipmapLevel (const SampleArguments& args, const Vec4& result, const Vec4& coord, const Vec2& lodBounds, int level, std::ostream& report) const { DE_ASSERT(level < m_imParams.levels); VkSamplerMipmapMode mipmapFilter = m_samplerParams.mipmapFilter; if (level == m_imParams.levels - 1) { mipmapFilter = VK_SAMPLER_MIPMAP_MODE_NEAREST; } Vec3 unnormalizedCoordMin[2]; Vec3 unnormalizedCoordMax[2]; IVec3 gridCoordMin[2]; IVec3 gridCoordMax[2]; const FloatFormat coordFormat(-32, 32, 16, true); calcUnnormalizedCoordRange(coord, m_levels[level].getSize(), coordFormat, unnormalizedCoordMin[0], unnormalizedCoordMax[0]); calcTexelGridCoordRange(unnormalizedCoordMin[0], unnormalizedCoordMax[0], m_coordBits, gridCoordMin[0], gridCoordMax[0]); report << "Level " << level << " computed unnormalized coordinate range: [" << unnormalizedCoordMin[0] << ", " << unnormalizedCoordMax[0] << "]\n"; report << "Level " << level << " computed texel grid coordinate range: [" << gridCoordMin[0] << ", " << gridCoordMax[0] << "]\n"; if (mipmapFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR) { calcUnnormalizedCoordRange(coord, m_levels[level+1].getSize(), coordFormat, unnormalizedCoordMin[1], unnormalizedCoordMax[1]); calcTexelGridCoordRange(unnormalizedCoordMin[1], unnormalizedCoordMax[1], m_coordBits, gridCoordMin[1], gridCoordMax[1]); report << "Level " << level+1 << " computed unnormalized coordinate range: [" << unnormalizedCoordMin[1] << " - " << unnormalizedCoordMax[1] << "]\n"; report << "Level " << level+1 << " computed texel grid coordinate range: [" << gridCoordMin[1] << " - " << gridCoordMax[1] << "]\n"; } else { unnormalizedCoordMin[1] = unnormalizedCoordMax[1] = Vec3(0.0f); gridCoordMin[1] = gridCoordMax[1] = IVec3(0); } bool done = false; IVec3 gridCoord[2] = {gridCoordMin[0], gridCoordMin[1]}; while (!done) { if (verifySampleTexelGridCoords(args, result, gridCoord[0], gridCoord[1], lodBounds, level, mipmapFilter, report)) return true; // Get next grid coordinate to test at // Represents whether the increment at a position wraps and should "carry" to the next place bool carry = true; for (int levelNdx = 0; levelNdx < 2; ++levelNdx) { for (int compNdx = 0; compNdx < 3; ++compNdx) { if (carry) { deInt32& comp = gridCoord[levelNdx][compNdx]; ++comp; if (comp > gridCoordMax[levelNdx][compNdx]) { comp = gridCoordMin[levelNdx][compNdx]; } else { carry = false; } } } } done = carry; } return false; } bool SampleVerifier::verifySampleCubemapFace (const SampleArguments& args, const Vec4& result, const Vec4& coord, const Vec4& dPdx, const Vec4& dPdy, int face, std::ostream& report) const { // Will use this parameter once cubemapping is implemented completely DE_UNREF(face); Vec2 lodBounds; if (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) { float lodBias = m_samplerParams.lodBias; if (m_sampleLookupSettings.hasLodBias) lodBias += args.lodBias; lodBounds = calcLodBounds(dPdx.swizzle(0, 1, 2), dPdy.swizzle(0, 1, 2), m_imParams.size, lodBias, m_samplerParams.minLod, m_samplerParams.maxLod); } else { lodBounds[0] = lodBounds[1] = args.lod; } DE_ASSERT(lodBounds[0] <= lodBounds[1]); const UVec2 levelBounds = calcLevelBounds(lodBounds, m_imParams.levels, m_samplerParams.mipmapFilter); for (deUint32 level = levelBounds[0]; level <= levelBounds[1]; ++level) { report << "Testing at mipmap level " << level << "...\n"; const Vec2 levelLodBounds = calcLevelLodBounds(lodBounds, level); if (verifySampleMipmapLevel(args, result, coord, levelLodBounds, level, report)) { return true; } report << "Done testing mipmap level " << level << ".\n\n"; } return false; } bool SampleVerifier::verifySampleImpl (const SampleArguments& args, const Vec4& result, std::ostream& report) const { // \todo [2016-07-11 collinbaker] Handle depth and stencil formats // \todo [2016-07-06 collinbaker] Handle dRef DE_ASSERT(m_samplerParams.isCompare == false); Vec4 coord = args.coord; int coordSize = 0; if (m_imParams.dim == IMG_DIM_1D) { coordSize = 1; } else if (m_imParams.dim == IMG_DIM_2D) { coordSize = 2; } else if (m_imParams.dim == IMG_DIM_3D || m_imParams.dim == IMG_DIM_CUBE) { coordSize = 3; } // 15.6.1 Project operation if (m_sampleLookupSettings.isProjective) { DE_ASSERT(args.coord[coordSize] != 0.0f); const float proj = coord[coordSize]; coord = coord / proj; } const Vec4 dPdx = (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) ? args.dPdx : Vec4(0); const Vec4 dPdy = (m_sampleLookupSettings.lookupLodMode == LOOKUP_LOD_MODE_DERIVATIVES) ? args.dPdy : Vec4(0); // 15.6.3 Cube Map Face Selection and Transformations if (m_imParams.dim == IMG_DIM_CUBE) { const Vec3 r = coord.swizzle(0, 1, 2); const Vec3 drdx = dPdx.swizzle(0, 1, 2); const Vec3 drdy = dPdy.swizzle(0, 1, 2); int faceBitmap = calcCandidateCubemapFaces(r); // We must test every possible disambiguation order for (int faceNdx = 0; faceNdx < 6; ++faceNdx) { const bool isPossible = ((faceBitmap & (1U << faceNdx)) != 0); if (!isPossible) { continue; } Vec2 coordFace; Vec2 dPdxFace; Vec2 dPdyFace; calcCubemapFaceCoords(r, drdx, drdy, faceNdx, coordFace, dPdxFace, dPdyFace); if (verifySampleCubemapFace(args, result, Vec4(coordFace[0], coordFace[1], 0.0f, 0.0f), Vec4(dPdxFace[0], dPdxFace[1], 0.0f, 0.0f), Vec4(dPdyFace[0], dPdyFace[1], 0.0f, 0.0f), faceNdx, report)) { return true; } } return false; } else { return verifySampleCubemapFace(args, result, coord, dPdx, dPdy, 0, report); } } bool SampleVerifier::verifySampleReport (const SampleArguments& args, const Vec4& result, std::string& report) const { std::ostringstream reportStream; const bool isValid = verifySampleImpl(args, result, reportStream); report = reportStream.str(); return isValid; } bool SampleVerifier::verifySample (const SampleArguments& args, const Vec4& result) const { // Create unopened ofstream to simulate "null" ostream std::ofstream nullStream; return verifySampleImpl(args, result, nullStream); } } // texture } // vkt