/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES Utilities * ------------------------------------------------ * * Copyright 2014 The Android Open Source Project * * 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 Utility functions and structures for texture tests. This code * is originated from the modules/glshared/glsTextureTestUtil.hpp and it * is tightly coupled with the GLES and Vulkan texture tests! *//*--------------------------------------------------------------------*/ #include "gluTextureTestUtil.hpp" #include "tcuFloat.hpp" #include "tcuImageCompare.hpp" #include "tcuTestLog.hpp" #include "tcuVectorUtil.hpp" #include "deMath.h" #include "deStringUtil.hpp" #include using std::string; namespace glu { namespace TextureTestUtil { enum { MIN_SUBPIXEL_BITS = 4 }; SamplerType getSamplerType(tcu::TextureFormat format) { using tcu::TextureFormat; switch (format.type) { case TextureFormat::SIGNED_INT8: case TextureFormat::SIGNED_INT16: case TextureFormat::SIGNED_INT32: return SAMPLERTYPE_INT; case TextureFormat::UNSIGNED_INT8: case TextureFormat::UNSIGNED_INT32: case TextureFormat::UNSIGNED_INT_1010102_REV: return SAMPLERTYPE_UINT; // Texture formats used in depth/stencil textures. case TextureFormat::UNSIGNED_INT16: case TextureFormat::UNSIGNED_INT_24_8: return (format.order == TextureFormat::D || format.order == TextureFormat::DS) ? SAMPLERTYPE_FLOAT : SAMPLERTYPE_UINT; default: return SAMPLERTYPE_FLOAT; } } SamplerType getFetchSamplerType(tcu::TextureFormat format) { using tcu::TextureFormat; switch (format.type) { case TextureFormat::SIGNED_INT8: case TextureFormat::SIGNED_INT16: case TextureFormat::SIGNED_INT32: return SAMPLERTYPE_FETCH_INT; case TextureFormat::UNSIGNED_INT8: case TextureFormat::UNSIGNED_INT32: case TextureFormat::UNSIGNED_INT_1010102_REV: return SAMPLERTYPE_FETCH_UINT; // Texture formats used in depth/stencil textures. case TextureFormat::UNSIGNED_INT16: case TextureFormat::UNSIGNED_INT_24_8: return (format.order == TextureFormat::D || format.order == TextureFormat::DS) ? SAMPLERTYPE_FETCH_FLOAT : SAMPLERTYPE_FETCH_UINT; default: return SAMPLERTYPE_FETCH_FLOAT; } } static tcu::Texture1DView getSubView(const tcu::Texture1DView &view, int baseLevel, int maxLevel, tcu::ImageViewMinLodParams *minLodParams DE_UNUSED_ATTR) { const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels() - 1); const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels() - 1); const int numLevels = clampedMax - clampedBase + 1; return tcu::Texture1DView(numLevels, view.getLevels() + clampedBase); } static tcu::Texture2DView getSubView(const tcu::Texture2DView &view, int baseLevel, int maxLevel, tcu::ImageViewMinLodParams *minLodParams = DE_NULL) { const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels() - 1); const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels() - 1); const int numLevels = clampedMax - clampedBase + 1; return tcu::Texture2DView(numLevels, view.getLevels() + clampedBase, view.isES2(), minLodParams); } static tcu::TextureCubeView getSubView(const tcu::TextureCubeView &view, int baseLevel, int maxLevel, tcu::ImageViewMinLodParams *minLodParams = DE_NULL) { const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels() - 1); const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels() - 1); const int numLevels = clampedMax - clampedBase + 1; const tcu::ConstPixelBufferAccess *levels[tcu::CUBEFACE_LAST]; for (int face = 0; face < tcu::CUBEFACE_LAST; face++) levels[face] = view.getFaceLevels((tcu::CubeFace)face) + clampedBase; return tcu::TextureCubeView(numLevels, levels, false, minLodParams); } static tcu::Texture3DView getSubView(const tcu::Texture3DView &view, int baseLevel, int maxLevel, tcu::ImageViewMinLodParams *minLodParams = DE_NULL) { const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels() - 1); const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels() - 1); const int numLevels = clampedMax - clampedBase + 1; return tcu::Texture3DView(numLevels, view.getLevels() + clampedBase, false, minLodParams); } static tcu::TextureCubeArrayView getSubView(const tcu::TextureCubeArrayView &view, int baseLevel, int maxLevel, tcu::ImageViewMinLodParams *minLodParams DE_UNUSED_ATTR = DE_NULL) { const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels() - 1); const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels() - 1); const int numLevels = clampedMax - clampedBase + 1; return tcu::TextureCubeArrayView(numLevels, view.getLevels() + clampedBase); } inline float linearInterpolate(float t, float minVal, float maxVal) { return minVal + (maxVal - minVal) * t; } inline tcu::Vec4 linearInterpolate(float t, const tcu::Vec4 &a, const tcu::Vec4 &b) { return a + (b - a) * t; } inline float bilinearInterpolate(float x, float y, const tcu::Vec4 &quad) { float w00 = (1.0f - x) * (1.0f - y); float w01 = (1.0f - x) * y; float w10 = x * (1.0f - y); float w11 = x * y; return quad.x() * w00 + quad.y() * w10 + quad.z() * w01 + quad.w() * w11; } float triangleInterpolate(float v0, float v1, float v2, float x, float y) { return v0 + (v2 - v0) * x + (v1 - v0) * y; } float triangleInterpolate(const tcu::Vec3 &v, float x, float y) { return triangleInterpolate(v.x(), v.y(), v.z(), x, y); } // 1D lookup LOD computation. float computeLodFromDerivates(LodMode mode, float dudx, float dudy) { float p = 0.0f; switch (mode) { // \note [mika] Min and max bounds equal to exact with 1D textures case LODMODE_EXACT: case LODMODE_MIN_BOUND: case LODMODE_MAX_BOUND: p = de::max(deFloatAbs(dudx), deFloatAbs(dudy)); break; default: DE_ASSERT(false); } return deFloatLog2(p); } float computeNonProjectedTriLod(LodMode mode, const tcu::IVec2 &dstSize, int32_t srcSize, const tcu::Vec3 &sq) { float dux = (sq.z() - sq.x()) * (float)srcSize; float duy = (sq.y() - sq.x()) * (float)srcSize; float dx = (float)dstSize.x(); float dy = (float)dstSize.y(); return computeLodFromDerivates(mode, dux / dx, duy / dy); } // 2D lookup LOD computation. float computeLodFromDerivates(LodMode mode, float dudx, float dvdx, float dudy, float dvdy) { float p = 0.0f; switch (mode) { case LODMODE_EXACT: p = de::max(deFloatSqrt(dudx * dudx + dvdx * dvdx), deFloatSqrt(dudy * dudy + dvdy * dvdy)); break; case LODMODE_MIN_BOUND: case LODMODE_MAX_BOUND: { float mu = de::max(deFloatAbs(dudx), deFloatAbs(dudy)); float mv = de::max(deFloatAbs(dvdx), deFloatAbs(dvdy)); p = mode == LODMODE_MIN_BOUND ? de::max(mu, mv) : mu + mv; break; } default: DE_ASSERT(false); } return deFloatLog2(p); } float computeNonProjectedTriLod(LodMode mode, const tcu::IVec2 &dstSize, const tcu::IVec2 &srcSize, const tcu::Vec3 &sq, const tcu::Vec3 &tq) { float dux = (sq.z() - sq.x()) * (float)srcSize.x(); float duy = (sq.y() - sq.x()) * (float)srcSize.x(); float dvx = (tq.z() - tq.x()) * (float)srcSize.y(); float dvy = (tq.y() - tq.x()) * (float)srcSize.y(); float dx = (float)dstSize.x(); float dy = (float)dstSize.y(); return computeLodFromDerivates(mode, dux / dx, dvx / dx, duy / dy, dvy / dy); } // 3D lookup LOD computation. float computeLodFromDerivates(LodMode mode, float dudx, float dvdx, float dwdx, float dudy, float dvdy, float dwdy) { float p = 0.0f; switch (mode) { case LODMODE_EXACT: p = de::max(deFloatSqrt(dudx * dudx + dvdx * dvdx + dwdx * dwdx), deFloatSqrt(dudy * dudy + dvdy * dvdy + dwdy * dwdy)); break; case LODMODE_MIN_BOUND: case LODMODE_MAX_BOUND: { float mu = de::max(deFloatAbs(dudx), deFloatAbs(dudy)); float mv = de::max(deFloatAbs(dvdx), deFloatAbs(dvdy)); float mw = de::max(deFloatAbs(dwdx), deFloatAbs(dwdy)); p = mode == LODMODE_MIN_BOUND ? de::max(de::max(mu, mv), mw) : (mu + mv + mw); break; } default: DE_ASSERT(false); } return deFloatLog2(p); } float computeNonProjectedTriLod(LodMode mode, const tcu::IVec2 &dstSize, const tcu::IVec3 &srcSize, const tcu::Vec3 &sq, const tcu::Vec3 &tq, const tcu::Vec3 &rq) { float dux = (sq.z() - sq.x()) * (float)srcSize.x(); float duy = (sq.y() - sq.x()) * (float)srcSize.x(); float dvx = (tq.z() - tq.x()) * (float)srcSize.y(); float dvy = (tq.y() - tq.x()) * (float)srcSize.y(); float dwx = (rq.z() - rq.x()) * (float)srcSize.z(); float dwy = (rq.y() - rq.x()) * (float)srcSize.z(); float dx = (float)dstSize.x(); float dy = (float)dstSize.y(); return computeLodFromDerivates(mode, dux / dx, dvx / dx, dwx / dx, duy / dy, dvy / dy, dwy / dy); } static inline float projectedTriInterpolate(const tcu::Vec3 &s, const tcu::Vec3 &w, float nx, float ny) { return (s[0] * (1.0f - nx - ny) / w[0] + s[1] * ny / w[1] + s[2] * nx / w[2]) / ((1.0f - nx - ny) / w[0] + ny / w[1] + nx / w[2]); } static inline float triDerivateX(const tcu::Vec3 &s, const tcu::Vec3 &w, float wx, float width, float ny) { float d = w[1] * w[2] * (width * (ny - 1.0f) + wx) - w[0] * (w[2] * width * ny + w[1] * wx); return (w[0] * w[1] * w[2] * width * (w[1] * (s[0] - s[2]) * (ny - 1.0f) + ny * (w[2] * (s[1] - s[0]) + w[0] * (s[2] - s[1])))) / (d * d); } static inline float triDerivateY(const tcu::Vec3 &s, const tcu::Vec3 &w, float wy, float height, float nx) { float d = w[1] * w[2] * (height * (nx - 1.0f) + wy) - w[0] * (w[1] * height * nx + w[2] * wy); return (w[0] * w[1] * w[2] * height * (w[2] * (s[0] - s[1]) * (nx - 1.0f) + nx * (w[0] * (s[1] - s[2]) + w[1] * (s[2] - s[0])))) / (d * d); } // 1D lookup LOD. static float computeProjectedTriLod(LodMode mode, const tcu::Vec3 &u, const tcu::Vec3 &projection, float wx, float wy, float width, float height) { // Exact derivatives. float dudx = triDerivateX(u, projection, wx, width, wy / height); float dudy = triDerivateY(u, projection, wy, height, wx / width); return computeLodFromDerivates(mode, dudx, dudy); } // 2D lookup LOD. static float computeProjectedTriLod(LodMode mode, const tcu::Vec3 &u, const tcu::Vec3 &v, const tcu::Vec3 &projection, float wx, float wy, float width, float height) { // Exact derivatives. float dudx = triDerivateX(u, projection, wx, width, wy / height); float dvdx = triDerivateX(v, projection, wx, width, wy / height); float dudy = triDerivateY(u, projection, wy, height, wx / width); float dvdy = triDerivateY(v, projection, wy, height, wx / width); return computeLodFromDerivates(mode, dudx, dvdx, dudy, dvdy); } // 3D lookup LOD. static float computeProjectedTriLod(LodMode mode, const tcu::Vec3 &u, const tcu::Vec3 &v, const tcu::Vec3 &w, const tcu::Vec3 &projection, float wx, float wy, float width, float height) { // Exact derivatives. float dudx = triDerivateX(u, projection, wx, width, wy / height); float dvdx = triDerivateX(v, projection, wx, width, wy / height); float dwdx = triDerivateX(w, projection, wx, width, wy / height); float dudy = triDerivateY(u, projection, wy, height, wx / width); float dvdy = triDerivateY(v, projection, wy, height, wx / width); float dwdy = triDerivateY(w, projection, wy, height, wx / width); return computeLodFromDerivates(mode, dudx, dvdx, dwdx, dudy, dvdy, dwdy); } static inline tcu::Vec4 execSample(const tcu::Texture1DView &src, const ReferenceParams ¶ms, float s, float lod) { if (params.samplerType == SAMPLERTYPE_SHADOW) return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, lod), 0.0, 0.0, 1.0f); else return src.sample(params.sampler, s, lod); } static inline tcu::Vec4 execSample(const tcu::Texture2DView &src, const ReferenceParams ¶ms, float s, float t, float lod) { if (params.samplerType == SAMPLERTYPE_SHADOW) return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, lod), 0.0, 0.0, 1.0f); else return src.sample(params.sampler, s, t, lod); } static inline tcu::Vec4 execSample(const tcu::TextureCubeView &src, const ReferenceParams ¶ms, float s, float t, float r, float lod) { if (params.samplerType == SAMPLERTYPE_SHADOW) return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, lod), 0.0, 0.0, 1.0f); else return src.sample(params.sampler, s, t, r, lod); } static inline tcu::Vec4 execSample(const tcu::Texture2DArrayView &src, const ReferenceParams ¶ms, float s, float t, float r, float lod) { if (params.samplerType == SAMPLERTYPE_SHADOW) return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, lod), 0.0, 0.0, 1.0f); else return src.sample(params.sampler, s, t, r, lod); } static inline tcu::Vec4 execSample(const tcu::TextureCubeArrayView &src, const ReferenceParams ¶ms, float s, float t, float r, float q, float lod) { if (params.samplerType == SAMPLERTYPE_SHADOW) return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, q, lod), 0.0, 0.0, 1.0f); else return src.sample(params.sampler, s, t, r, q, lod); } static inline tcu::Vec4 execSample(const tcu::Texture1DArrayView &src, const ReferenceParams ¶ms, float s, float t, float lod) { if (params.samplerType == SAMPLERTYPE_SHADOW) return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, lod), 0.0, 0.0, 1.0f); else return src.sample(params.sampler, s, t, lod); } static void sampleTextureNonProjected(const tcu::SurfaceAccess &dst, const tcu::Texture1DView &rawSrc, const tcu::Vec4 &sq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture1DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); int srcSize = src.getWidth(); // Coordinates and lod per triangle. tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; float triLod[2] = {de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0]) + lodBias, params.minLod, params.maxLod), de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1]) + lodBias, params.minLod, params.maxLod)}; for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { float yf = ((float)y + 0.5f) / (float)dst.getHeight(); float xf = ((float)x + 0.5f) / (float)dst.getWidth(); int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. float triX = triNdx ? 1.0f - xf : xf; float triY = triNdx ? 1.0f - yf : yf; float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); float lod = triLod[triNdx]; dst.setPixel(execSample(src, params, s, lod) * params.colorScale + params.colorBias, x, y); } } } template static void sampleTextureNonProjected(const PixelAccess &dst, const tcu::Texture2DView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; tcu::Texture2DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); // Coordinates and lod per triangle. tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0]) + lodBias, params.minLod, params.maxLod), de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1]) + lodBias, params.minLod, params.maxLod)}; for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { float yf = ((float)y + 0.5f) / (float)dst.getHeight(); float xf = ((float)x + 0.5f) / (float)dst.getWidth(); int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. float triX = triNdx ? 1.0f - xf : xf; float triY = triNdx ? 1.0f - yf : yf; float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); float lod = triLod[triNdx]; if (params.imageViewMinLod != 0.0f && params.samplerType == SAMPLERTYPE_FETCH_FLOAT) lod = (float)params.lodTexelFetch; if (params.float16TexCoord) { s = tcu::Float16(s, tcu::ROUND_TO_ZERO).asFloat(); t = tcu::Float16(t, tcu::ROUND_TO_ZERO).asFloat(); } dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, x, y); } } } static void sampleTextureProjected(const tcu::SurfaceAccess &dst, const tcu::Texture1DView &rawSrc, const tcu::Vec4 &sq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture1DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; float dstW = (float)dst.getWidth(); float dstH = (float)dst.getHeight(); tcu::Vec4 uq = sq * (float)src.getWidth(); tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triU[2] = {uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1)}; tcu::Vec3 triW[2] = {params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1)}; for (int py = 0; py < dst.getHeight(); py++) { for (int px = 0; px < dst.getWidth(); px++) { float wx = (float)px + 0.5f; float wy = (float)py + 0.5f; float nx = wx / dstW; float ny = wy / dstH; int triNdx = nx + ny >= 1.0f ? 1 : 0; float triWx = triNdx ? dstW - wx : wx; float triWy = triNdx ? dstH - wy : wy; float triNx = triNdx ? 1.0f - nx : nx; float triNy = triNdx ? 1.0f - ny : ny; float s = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy); float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triW[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight()) + lodBias; dst.setPixel(execSample(src, params, s, lod) * params.colorScale + params.colorBias, px, py); } } } template static void sampleTextureProjected(const PixelAccess &dst, const tcu::Texture2DView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture2DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; float dstW = (float)dst.getWidth(); float dstH = (float)dst.getHeight(); tcu::Vec4 uq = sq * (float)src.getWidth(); tcu::Vec4 vq = tq * (float)src.getHeight(); tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; tcu::Vec3 triU[2] = {uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1)}; tcu::Vec3 triV[2] = {vq.swizzle(0, 1, 2), vq.swizzle(3, 2, 1)}; tcu::Vec3 triW[2] = {params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1)}; for (int py = 0; py < dst.getHeight(); py++) { for (int px = 0; px < dst.getWidth(); px++) { float wx = (float)px + 0.5f; float wy = (float)py + 0.5f; float nx = wx / dstW; float ny = wy / dstH; int triNdx = nx + ny >= 1.0f ? 1 : 0; float triWx = triNdx ? dstW - wx : wx; float triWy = triNdx ? dstH - wy : wy; float triNx = triNdx ? 1.0f - nx : nx; float triNy = triNdx ? 1.0f - ny : ny; float s = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy); float t = projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy); float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triV[triNdx], triW[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight()) + lodBias; dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, px, py); } } } void sampleTexture(const tcu::PixelBufferAccess &dst, const tcu::Texture2DView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::ImageViewMinLodParams minLodParams = { params.baseLevel, // int baseLevel; { params.imageViewMinLod, // float minLod; params.imageViewMinLodMode, // ImageViewMinLodMode }, params.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::Texture2DView view = getSubView(src, params.baseLevel, params.maxLevel, params.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); if (params.flags & ReferenceParams::PROJECTED) sampleTextureProjected(dst, view, sq, tq, params); else sampleTextureNonProjected(dst, view, sq, tq, params); } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::Texture2DView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::ImageViewMinLodParams minLodParams = { params.baseLevel, // int baseLevel; { params.imageViewMinLod, // float minLod; params.imageViewMinLodMode, // ImageViewMinLodMode }, params.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::Texture2DView view = getSubView(src, params.baseLevel, params.maxLevel, params.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); if (params.flags & ReferenceParams::PROJECTED) sampleTextureProjected(dst, view, sq, tq, params); else sampleTextureNonProjected(dst, view, sq, tq, params); } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::Texture1DView &src, const float *texCoord, const ReferenceParams ¶ms) { const tcu::Texture1DView view = getSubView(src, params.baseLevel, params.maxLevel, DE_NULL); const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); if (params.flags & ReferenceParams::PROJECTED) sampleTextureProjected(dst, view, sq, params); else sampleTextureNonProjected(dst, view, sq, params); } static float computeCubeLodFromDerivates(LodMode lodMode, const tcu::Vec3 &coord, const tcu::Vec3 &coordDx, const tcu::Vec3 &coordDy, const int faceSize) { const tcu::CubeFace face = tcu::selectCubeFace(coord); int maNdx = 0; int sNdx = 0; int tNdx = 0; // \note Derivate signs don't matter when computing lod switch (face) { case tcu::CUBEFACE_NEGATIVE_X: case tcu::CUBEFACE_POSITIVE_X: maNdx = 0; sNdx = 2; tNdx = 1; break; case tcu::CUBEFACE_NEGATIVE_Y: case tcu::CUBEFACE_POSITIVE_Y: maNdx = 1; sNdx = 0; tNdx = 2; break; case tcu::CUBEFACE_NEGATIVE_Z: case tcu::CUBEFACE_POSITIVE_Z: maNdx = 2; sNdx = 0; tNdx = 1; break; default: DE_ASSERT(false); } { const float sc = coord[sNdx]; const float tc = coord[tNdx]; const float ma = de::abs(coord[maNdx]); const float scdx = coordDx[sNdx]; const float tcdx = coordDx[tNdx]; const float madx = de::abs(coordDx[maNdx]); const float scdy = coordDy[sNdx]; const float tcdy = coordDy[tNdx]; const float mady = de::abs(coordDy[maNdx]); const float dudx = float(faceSize) * 0.5f * (scdx * ma - sc * madx) / (ma * ma); const float dvdx = float(faceSize) * 0.5f * (tcdx * ma - tc * madx) / (ma * ma); const float dudy = float(faceSize) * 0.5f * (scdy * ma - sc * mady) / (ma * ma); const float dvdy = float(faceSize) * 0.5f * (tcdy * ma - tc * mady) / (ma * ma); return computeLodFromDerivates(lodMode, dudx, dvdx, dudy, dvdy); } } static void sampleTextureCube(const tcu::SurfaceAccess &dst, const tcu::TextureCubeView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const tcu::Vec4 &rq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::TextureCubeView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); const tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const int srcSize = src.getSize(); // Coordinates per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1)}; const float lodBias((params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f); for (int py = 0; py < dst.getHeight(); py++) { for (int px = 0; px < dst.getWidth(); px++) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(triangleInterpolate(triS[triNdx], triNx, triNy), triangleInterpolate(triT[triNdx], triNx, triNy), triangleInterpolate(triR[triNdx], triNx, triNy)); const tcu::Vec3 coordDx(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); const tcu::Vec3 coordDy(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); const float lod = de::clamp(computeCubeLodFromDerivates(params.lodMode, coord, coordDx, coordDy, srcSize) + lodBias, params.minLod, params.maxLod); dst.setPixel(execSample(src, params, coord.x(), coord.y(), coord.z(), lod) * params.colorScale + params.colorBias, px, py); } } } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::TextureCubeView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::ImageViewMinLodParams minLodParams = { params.baseLevel, // int baseLevel; { params.imageViewMinLod, // float minLod; params.imageViewMinLodMode, // ImageViewMinLodMode }, params.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::TextureCubeView view = getSubView(src, params.baseLevel, params.maxLevel, params.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); return sampleTextureCube(dst, view, sq, tq, rq, params); } static void sampleTextureNonProjected(const tcu::SurfaceAccess &dst, const tcu::Texture2DArrayView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const tcu::Vec4 &rq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture2DArrayView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); // Coordinates and lod per triangle. tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0]) + lodBias, params.minLod, params.maxLod), de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1]) + lodBias, params.minLod, params.maxLod)}; for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { float yf = ((float)y + 0.5f) / (float)dst.getHeight(); float xf = ((float)x + 0.5f) / (float)dst.getWidth(); int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. float triX = triNdx ? 1.0f - xf : xf; float triY = triNdx ? 1.0f - yf : yf; float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); float r = triangleInterpolate(triR[triNdx].x(), triR[triNdx].y(), triR[triNdx].z(), triX, triY); float lod = triLod[triNdx]; dst.setPixel(execSample(src, params, s, t, r, lod) * params.colorScale + params.colorBias, x, y); } } } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::Texture2DArrayView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); DE_ASSERT(!(params.flags & ReferenceParams::PROJECTED)); // \todo [2012-02-17 pyry] Support projected lookups. sampleTextureNonProjected(dst, src, sq, tq, rq, params); } static void sampleTextureNonProjected(const tcu::SurfaceAccess &dst, const tcu::Texture1DArrayView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture1DArrayView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); int32_t srcSize = src.getWidth(); // Coordinates and lod per triangle. tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; float triLod[2] = {computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0]) + lodBias, computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1]) + lodBias}; for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { float yf = ((float)y + 0.5f) / (float)dst.getHeight(); float xf = ((float)x + 0.5f) / (float)dst.getWidth(); int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. float triX = triNdx ? 1.0f - xf : xf; float triY = triNdx ? 1.0f - yf : yf; float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); float lod = triLod[triNdx]; dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, x, y); } } } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::Texture1DArrayView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); DE_ASSERT(!(params.flags & ReferenceParams::PROJECTED)); // \todo [2014-06-09 mika] Support projected lookups. sampleTextureNonProjected(dst, src, sq, tq, params); } static void sampleTextureNonProjected(const tcu::SurfaceAccess &dst, const tcu::Texture3DView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const tcu::Vec4 &rq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture3DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); tcu::IVec3 srcSize = tcu::IVec3(src.getWidth(), src.getHeight(), src.getDepth()); // Coordinates and lod per triangle. tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0], triR[0]) + lodBias, params.minLod, params.maxLod), de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1], triR[1]) + lodBias, params.minLod, params.maxLod)}; for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { float yf = ((float)y + 0.5f) / (float)dst.getHeight(); float xf = ((float)x + 0.5f) / (float)dst.getWidth(); int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. float triX = triNdx ? 1.0f - xf : xf; float triY = triNdx ? 1.0f - yf : yf; float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); float r = triangleInterpolate(triR[triNdx].x(), triR[triNdx].y(), triR[triNdx].z(), triX, triY); float lod = triLod[triNdx]; if (params.imageViewMinLod != 0.0f && params.samplerType == SAMPLERTYPE_FETCH_FLOAT) lod = (float)params.lodTexelFetch; dst.setPixel(src.sample(params.sampler, s, t, r, lod) * params.colorScale + params.colorBias, x, y); } } } static void sampleTextureProjected(const tcu::SurfaceAccess &dst, const tcu::Texture3DView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const tcu::Vec4 &rq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::Texture3DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; float dstW = (float)dst.getWidth(); float dstH = (float)dst.getHeight(); tcu::Vec4 uq = sq * (float)src.getWidth(); tcu::Vec4 vq = tq * (float)src.getHeight(); tcu::Vec4 wq = rq * (float)src.getDepth(); tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; tcu::Vec3 triU[2] = {uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1)}; tcu::Vec3 triV[2] = {vq.swizzle(0, 1, 2), vq.swizzle(3, 2, 1)}; tcu::Vec3 triW[2] = {wq.swizzle(0, 1, 2), wq.swizzle(3, 2, 1)}; tcu::Vec3 triP[2] = {params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1)}; for (int py = 0; py < dst.getHeight(); py++) { for (int px = 0; px < dst.getWidth(); px++) { float wx = (float)px + 0.5f; float wy = (float)py + 0.5f; float nx = wx / dstW; float ny = wy / dstH; int triNdx = nx + ny >= 1.0f ? 1 : 0; float triWx = triNdx ? dstW - wx : wx; float triWy = triNdx ? dstH - wy : wy; float triNx = triNdx ? 1.0f - nx : nx; float triNy = triNdx ? 1.0f - ny : ny; float s = projectedTriInterpolate(triS[triNdx], triP[triNdx], triNx, triNy); float t = projectedTriInterpolate(triT[triNdx], triP[triNdx], triNx, triNy); float r = projectedTriInterpolate(triR[triNdx], triP[triNdx], triNx, triNy); float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triV[triNdx], triW[triNdx], triP[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight()) + lodBias; dst.setPixel(src.sample(params.sampler, s, t, r, lod) * params.colorScale + params.colorBias, px, py); } } } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::Texture3DView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::ImageViewMinLodParams minLodParams = { params.baseLevel, // int baseLevel; { params.imageViewMinLod, // float minLod; params.imageViewMinLodMode, // ImageViewMinLodMode }, params.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::Texture3DView view = getSubView(src, params.baseLevel, params.maxLevel, params.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); if (params.flags & ReferenceParams::PROJECTED) sampleTextureProjected(dst, view, sq, tq, rq, params); else sampleTextureNonProjected(dst, view, sq, tq, rq, params); } static void sampleTextureCubeArray(const tcu::SurfaceAccess &dst, const tcu::TextureCubeArrayView &rawSrc, const tcu::Vec4 &sq, const tcu::Vec4 &tq, const tcu::Vec4 &rq, const tcu::Vec4 &qq, const ReferenceParams ¶ms) { // Separate combined DS formats std::vector srcLevelStorage; const tcu::TextureCubeArrayView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); const float dstW = (float)dst.getWidth(); const float dstH = (float)dst.getHeight(); // Coordinates per triangle. tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; tcu::Vec3 triQ[2] = {qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1)}; const float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; for (int py = 0; py < dst.getHeight(); py++) { for (int px = 0; px < dst.getWidth(); px++) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(triangleInterpolate(triS[triNdx], triNx, triNy), triangleInterpolate(triT[triNdx], triNx, triNy), triangleInterpolate(triR[triNdx], triNx, triNy)); const float coordQ = triangleInterpolate(triQ[triNdx], triNx, triNy); const tcu::Vec3 coordDx(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); const tcu::Vec3 coordDy(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); const float lod = de::clamp(computeCubeLodFromDerivates(params.lodMode, coord, coordDx, coordDy, src.getSize()) + lodBias, params.minLod, params.maxLod); dst.setPixel(execSample(src, params, coord.x(), coord.y(), coord.z(), coordQ, lod) * params.colorScale + params.colorBias, px, py); } } } void sampleTexture(const tcu::SurfaceAccess &dst, const tcu::TextureCubeArrayView &src, const float *texCoord, const ReferenceParams ¶ms) { tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[4 + 0], texCoord[8 + 0], texCoord[12 + 0]); tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[4 + 1], texCoord[8 + 1], texCoord[12 + 1]); tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[4 + 2], texCoord[8 + 2], texCoord[12 + 2]); tcu::Vec4 qq = tcu::Vec4(texCoord[0 + 3], texCoord[4 + 3], texCoord[8 + 3], texCoord[12 + 3]); sampleTextureCubeArray(dst, src, sq, tq, rq, qq, params); } void fetchTexture(const tcu::SurfaceAccess &dst, const tcu::ConstPixelBufferAccess &src, const float *texCoord, const tcu::Vec4 &colorScale, const tcu::Vec4 &colorBias) { const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { const float yf = ((float)y + 0.5f) / (float)dst.getHeight(); const float xf = ((float)x + 0.5f) / (float)dst.getWidth(); const int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. const float triX = triNdx ? 1.0f - xf : xf; const float triY = triNdx ? 1.0f - yf : yf; const float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); dst.setPixel(src.getPixel((int)s, 0) * colorScale + colorBias, x, y); } } } bool compareImages(tcu::TestLog &log, const tcu::Surface &reference, const tcu::Surface &rendered, tcu::RGBA threshold) { return tcu::pixelThresholdCompare(log, "Result", "Image comparison result", reference, rendered, threshold, tcu::COMPARE_LOG_RESULT); } bool compareImages(tcu::TestLog &log, const char *name, const char *desc, const tcu::Surface &reference, const tcu::Surface &rendered, tcu::RGBA threshold) { return tcu::pixelThresholdCompare(log, name, desc, reference, rendered, threshold, tcu::COMPARE_LOG_RESULT); } int measureAccuracy(tcu::TestLog &log, const tcu::Surface &reference, const tcu::Surface &rendered, int bestScoreDiff, int worstScoreDiff) { return tcu::measurePixelDiffAccuracy(log, "Result", "Image comparison result", reference, rendered, bestScoreDiff, worstScoreDiff, tcu::COMPARE_LOG_EVERYTHING); } inline int rangeDiff(int x, int a, int b) { if (x < a) return a - x; else if (x > b) return x - b; else return 0; } inline tcu::RGBA rangeDiff(tcu::RGBA p, tcu::RGBA a, tcu::RGBA b) { int rMin = de::min(a.getRed(), b.getRed()); int rMax = de::max(a.getRed(), b.getRed()); int gMin = de::min(a.getGreen(), b.getGreen()); int gMax = de::max(a.getGreen(), b.getGreen()); int bMin = de::min(a.getBlue(), b.getBlue()); int bMax = de::max(a.getBlue(), b.getBlue()); int aMin = de::min(a.getAlpha(), b.getAlpha()); int aMax = de::max(a.getAlpha(), b.getAlpha()); return tcu::RGBA(rangeDiff(p.getRed(), rMin, rMax), rangeDiff(p.getGreen(), gMin, gMax), rangeDiff(p.getBlue(), bMin, bMax), rangeDiff(p.getAlpha(), aMin, aMax)); } inline bool rangeCompare(tcu::RGBA p, tcu::RGBA a, tcu::RGBA b, tcu::RGBA threshold) { tcu::RGBA diff = rangeDiff(p, a, b); return diff.getRed() <= threshold.getRed() && diff.getGreen() <= threshold.getGreen() && diff.getBlue() <= threshold.getBlue() && diff.getAlpha() <= threshold.getAlpha(); } void computeQuadTexCoord1D(std::vector &dst, float left, float right) { dst.resize(4); dst[0] = left; dst[1] = left; dst[2] = right; dst[3] = right; } void computeQuadTexCoord1DArray(std::vector &dst, int layerNdx, float left, float right) { dst.resize(4 * 2); dst[0] = left; dst[1] = (float)layerNdx; dst[2] = left; dst[3] = (float)layerNdx; dst[4] = right; dst[5] = (float)layerNdx; dst[6] = right; dst[7] = (float)layerNdx; } void computeQuadTexCoord2D(std::vector &dst, const tcu::Vec2 &bottomLeft, const tcu::Vec2 &topRight) { dst.resize(4 * 2); dst[0] = bottomLeft.x(); dst[1] = bottomLeft.y(); dst[2] = bottomLeft.x(); dst[3] = topRight.y(); dst[4] = topRight.x(); dst[5] = bottomLeft.y(); dst[6] = topRight.x(); dst[7] = topRight.y(); } void computeQuadTexCoord2DArray(std::vector &dst, int layerNdx, const tcu::Vec2 &bottomLeft, const tcu::Vec2 &topRight) { dst.resize(4 * 3); dst[0] = bottomLeft.x(); dst[1] = bottomLeft.y(); dst[2] = (float)layerNdx; dst[3] = bottomLeft.x(); dst[4] = topRight.y(); dst[5] = (float)layerNdx; dst[6] = topRight.x(); dst[7] = bottomLeft.y(); dst[8] = (float)layerNdx; dst[9] = topRight.x(); dst[10] = topRight.y(); dst[11] = (float)layerNdx; } void computeQuadTexCoord3D(std::vector &dst, const tcu::Vec3 &p0, const tcu::Vec3 &p1, const tcu::IVec3 &dirSwz) { tcu::Vec3 f0 = tcu::Vec3(0.0f, 0.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); tcu::Vec3 f1 = tcu::Vec3(0.0f, 1.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); tcu::Vec3 f2 = tcu::Vec3(1.0f, 0.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); tcu::Vec3 f3 = tcu::Vec3(1.0f, 1.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); tcu::Vec3 v0 = p0 + (p1 - p0) * f0; tcu::Vec3 v1 = p0 + (p1 - p0) * f1; tcu::Vec3 v2 = p0 + (p1 - p0) * f2; tcu::Vec3 v3 = p0 + (p1 - p0) * f3; dst.resize(4 * 3); dst[0] = v0.x(); dst[1] = v0.y(); dst[2] = v0.z(); dst[3] = v1.x(); dst[4] = v1.y(); dst[5] = v1.z(); dst[6] = v2.x(); dst[7] = v2.y(); dst[8] = v2.z(); dst[9] = v3.x(); dst[10] = v3.y(); dst[11] = v3.z(); } void computeQuadTexCoordCube(std::vector &dst, tcu::CubeFace face) { static const float texCoordNegX[] = {-1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f}; static const float texCoordPosX[] = {+1.0f, 1.0f, 1.0f, +1.0f, -1.0f, 1.0f, +1.0f, 1.0f, -1.0f, +1.0f, -1.0f, -1.0f}; static const float texCoordNegY[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f}; static const float texCoordPosY[] = {-1.0f, +1.0f, -1.0f, -1.0f, +1.0f, 1.0f, 1.0f, +1.0f, -1.0f, 1.0f, +1.0f, 1.0f}; static const float texCoordNegZ[] = {1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f}; static const float texCoordPosZ[] = {-1.0f, 1.0f, +1.0f, -1.0f, -1.0f, +1.0f, 1.0f, 1.0f, +1.0f, 1.0f, -1.0f, +1.0f}; const float *texCoord = DE_NULL; int texCoordSize = DE_LENGTH_OF_ARRAY(texCoordNegX); switch (face) { case tcu::CUBEFACE_NEGATIVE_X: texCoord = texCoordNegX; break; case tcu::CUBEFACE_POSITIVE_X: texCoord = texCoordPosX; break; case tcu::CUBEFACE_NEGATIVE_Y: texCoord = texCoordNegY; break; case tcu::CUBEFACE_POSITIVE_Y: texCoord = texCoordPosY; break; case tcu::CUBEFACE_NEGATIVE_Z: texCoord = texCoordNegZ; break; case tcu::CUBEFACE_POSITIVE_Z: texCoord = texCoordPosZ; break; default: DE_ASSERT(false); return; } dst.resize(texCoordSize); std::copy(texCoord, texCoord + texCoordSize, dst.begin()); } void computeQuadTexCoordCube(std::vector &dst, tcu::CubeFace face, const tcu::Vec2 &bottomLeft, const tcu::Vec2 &topRight) { int sRow = 0; int tRow = 0; int mRow = 0; float sSign = 1.0f; float tSign = 1.0f; float mSign = 1.0f; switch (face) { case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1; sSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2; break; case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1; tSign = -1.0f; break; default: DE_ASSERT(false); return; } dst.resize(3 * 4); dst[0 + mRow] = mSign; dst[3 + mRow] = mSign; dst[6 + mRow] = mSign; dst[9 + mRow] = mSign; dst[0 + sRow] = sSign * bottomLeft.x(); dst[3 + sRow] = sSign * bottomLeft.x(); dst[6 + sRow] = sSign * topRight.x(); dst[9 + sRow] = sSign * topRight.x(); dst[0 + tRow] = tSign * bottomLeft.y(); dst[3 + tRow] = tSign * topRight.y(); dst[6 + tRow] = tSign * bottomLeft.y(); dst[9 + tRow] = tSign * topRight.y(); } void computeQuadTexCoordCubeArray(std::vector &dst, tcu::CubeFace face, const tcu::Vec2 &bottomLeft, const tcu::Vec2 &topRight, const tcu::Vec2 &layerRange) { int sRow = 0; int tRow = 0; int mRow = 0; const int qRow = 3; float sSign = 1.0f; float tSign = 1.0f; float mSign = 1.0f; const float l0 = layerRange.x(); const float l1 = layerRange.y(); switch (face) { case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1; sSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2; break; case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f; break; case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1; tSign = -1.0f; break; default: DE_ASSERT(false); return; } dst.resize(4 * 4); dst[0 + mRow] = mSign; dst[4 + mRow] = mSign; dst[8 + mRow] = mSign; dst[12 + mRow] = mSign; dst[0 + sRow] = sSign * bottomLeft.x(); dst[4 + sRow] = sSign * bottomLeft.x(); dst[8 + sRow] = sSign * topRight.x(); dst[12 + sRow] = sSign * topRight.x(); dst[0 + tRow] = tSign * bottomLeft.y(); dst[4 + tRow] = tSign * topRight.y(); dst[8 + tRow] = tSign * bottomLeft.y(); dst[12 + tRow] = tSign * topRight.y(); if (l0 != l1) { dst[0 + qRow] = l0; dst[4 + qRow] = l0 * 0.5f + l1 * 0.5f; dst[8 + qRow] = l0 * 0.5f + l1 * 0.5f; dst[12 + qRow] = l1; } else { dst[0 + qRow] = l0; dst[4 + qRow] = l0; dst[8 + qRow] = l0; dst[12 + qRow] = l0; } } // Texture result verification //! Verifies texture lookup results and returns number of failed pixels. int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture1DView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; const tcu::Texture1DView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel, DE_NULL), srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const int srcSize = src.getWidth(); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const float coord = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy); const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * float(srcSize); const float coordDy = triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx) * float(srcSize); tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDy, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * float(srcSize); const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * float(srcSize); const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture2DView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; tcu::ImageViewMinLodParams minLodParams = { sampleParams.baseLevel, // int baseLevel; { sampleParams.imageViewMinLod, // float minLod; sampleParams.imageViewMinLodMode, // ImageViewMinLodMode }, sampleParams.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::Texture2DView view = getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel, sampleParams.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL); const tcu::Texture2DView src = getEffectiveTextureView(view, srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); // imageViewMinLodRel is used to calculate the image level to sample from, when VK_EXT_image_view_min_lod extension is enabled. // The value is relative to baseLevel as the Texture*View 'src' was created as the baseLevel being level[0]. const float imageViewMinLodRel = sampleParams.imageViewMinLod - (float)sampleParams.baseLevel; // We need to adapt ImageView's minLod value to the mipmap mdoe (i.e. nearest or linear) so we clamp with the right minLod value later. const float imageViewMinLodRelMode = tcu::isSamplerMipmapModeLinear(sampleParams.sampler.minFilter) ? deFloatFloor(imageViewMinLodRel) : (float)deClamp32((int)deFloatCeil(imageViewMinLodRel + 0.5f) - 1, sampleParams.baseLevel, sampleParams.maxLevel); const float minLod = (sampleParams.imageViewMinLod != 0.0f) ? de::max(imageViewMinLodRelMode, sampleParams.minLod) : sampleParams.minLod; const float posEps = 1.0f / float(1 << MIN_SUBPIXEL_BITS); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const bool tri0 = (wx - posEps) / dstW + (wy - posEps) / dstH <= 1.0f; const bool tri1 = (wx + posEps) / dstW + (wy + posEps) / dstH >= 1.0f; bool isOk = false; DE_ASSERT(tri0 || tri1); // Pixel can belong to either of the triangles if it lies close enough to the edge. for (int triNdx = (tri0 ? 0 : 1); triNdx <= (tri1 ? 1 : 0); triNdx++) { const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec2 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(minLod, sampleParams.maxLod), lodPrec); if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix)) { isOk = true; break; } } if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::Texture1DView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::Texture2DView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } //! Verifies texture lookup results and returns number of failed pixels. int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::TextureCubeView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; tcu::ImageViewMinLodParams minLodParams = { sampleParams.baseLevel, // int baseLevel; { sampleParams.imageViewMinLod, // float minLod; sampleParams.imageViewMinLodMode, // ImageViewMinLodMode }, sampleParams.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::TextureCubeView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel, sampleParams.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL), srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const int srcSize = src.getSize(); // Coordinates per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); const float posEps = 1.0f / float(1 << MIN_SUBPIXEL_BITS); // imageViewMinLodRel is used to calculate the image level to sample from, when VK_EXT_image_view_min_lod extension is enabled. // The value is relative to baseLevel as the Texture*View 'src' was created as the baseLevel being level[0]. const float imageViewMinLodRel = sampleParams.imageViewMinLod - (float)sampleParams.baseLevel; // We need to adapt ImageView's minLod value to the mipmap mdoe (i.e. nearest or linear) so we clamp with the right minLod value later. const float imageViewMinLodRelMode = tcu::isSamplerMipmapModeLinear(sampleParams.sampler.minFilter) ? deFloatFloor(imageViewMinLodRel) : (float)deClamp32((int)deFloatCeil(imageViewMinLodRel + 0.5f) - 1, sampleParams.baseLevel, sampleParams.maxLevel); const float minLod = (sampleParams.imageViewMinLod != 0.0f) ? de::max(imageViewMinLodRelMode, sampleParams.minLod) : sampleParams.minLod; int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), // \note Not strictly allowed by spec, but implementations do this in practice. tcu::Vec2(-1, -1), tcu::Vec2(-1, +1), tcu::Vec2(+1, -1), tcu::Vec2(+1, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const bool tri0 = (wx - posEps) / dstW + (wy - posEps) / dstH <= 1.0f; const bool tri1 = (wx + posEps) / dstW + (wy + posEps) / dstH >= 1.0f; bool isOk = false; DE_ASSERT(tri0 || tri1); // Pixel can belong to either of the triangles if it lies close enough to the edge. for (int triNdx = (tri0 ? 0 : 1); triNdx <= (tri1 ? 1 : 0); triNdx++) { const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec3 coordDx(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); const tcu::Vec3 coordDy(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord, coordDx, coordDy, srcSize, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec3 coordO(projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); const tcu::Vec3 coordDxo(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); const tcu::Vec3 coordDyo(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(minLod, sampleParams.maxLod), lodPrec); if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix)) { isOk = true; break; } } if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::TextureCubeView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } //! Verifies texture lookup results and returns number of failed pixels. int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture3DView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; tcu::ImageViewMinLodParams minLodParams = { sampleParams.baseLevel, // int baseLevel; { sampleParams.imageViewMinLod, // float minLod; sampleParams.imageViewMinLodMode, // ImageViewMinLodMode }, sampleParams.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::Texture3DView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel, sampleParams.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL), srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const tcu::IVec3 srcSize = tcu::IVec3(src.getWidth(), src.getHeight(), src.getDepth()); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); // imageViewMinLodRel is used to calculate the image level to sample from, when VK_EXT_image_view_min_lod extension is enabled. // The value is relative to baseLevel as the Texture*View 'src' was created as the baseLevel being level[0]. const float imageViewMinLodRel = sampleParams.imageViewMinLod - (float)sampleParams.baseLevel; // We need to adapt ImageView's minLod value to the mipmap mdoe (i.e. nearest or linear) so we clamp with the right minLod value later. const float imageViewMinLodRelMode = tcu::isSamplerMipmapModeLinear(sampleParams.sampler.minFilter) ? deFloatFloor(imageViewMinLodRel) : (float)deClamp32((int)deFloatCeil(imageViewMinLodRel + 0.5f) - 1, sampleParams.baseLevel, sampleParams.maxLevel); const float minLod = (sampleParams.imageViewMinLod != 0.0f) ? de::max(imageViewMinLodRelMode, sampleParams.minLod) : sampleParams.minLod; const float posEps = 1.0f / float(1 << MIN_SUBPIXEL_BITS); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const bool tri0 = (wx - posEps) / dstW + (wy - posEps) / dstH <= 1.0f; const bool tri1 = (wx + posEps) / dstW + (wy + posEps) / dstH >= 1.0f; bool isOk = false; DE_ASSERT(tri0 || tri1); // Pixel can belong to either of the triangles if it lies close enough to the edge. for (int triNdx = (tri0 ? 0 : 1); triNdx <= (tri1 ? 1 : 0); triNdx++) { const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec3 coordDx = tcu::Vec3(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); const tcu::Vec3 coordDy = tcu::Vec3(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates( coordDx.x(), coordDx.y(), coordDx.z(), coordDy.x(), coordDy.y(), coordDy.z(), lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec3 coordDxo = tcu::Vec3(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); const tcu::Vec3 coordDyo = tcu::Vec3(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDxo.z(), coordDyo.x(), coordDyo.y(), coordDyo.z(), lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(minLod, sampleParams.maxLod), lodPrec); if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix)) { isOk = true; break; } } if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::Texture3DView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } //! Verifies texture lookup results and returns number of failed pixels. int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture1DArrayView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; const tcu::Texture1DArrayView src = getEffectiveTextureView(baseView, srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const float srcSize = float(src.getWidth()); // For lod computation, thus #layers is ignored. // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec2 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * srcSize; const float coordDy = triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx) * srcSize; tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDy, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * srcSize; const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * srcSize; const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } //! Verifies texture lookup results and returns number of failed pixels. int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture2DArrayView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; const tcu::Texture2DArrayView src = getEffectiveTextureView(baseView, srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const tcu::Vec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()).asFloat(); // For lod computation, thus #layers is ignored. // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize; const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize; tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize; const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize; const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::Texture1DArrayView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::Texture2DArrayView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } //! Verifies texture lookup results and returns number of failed pixels. int computeTextureLookupDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::TextureCubeArrayView &baseView, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::IVec4 &coordBits, const tcu::LodPrecision &lodPrec, qpWatchDog *watchDog) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); std::vector srcLevelStorage; tcu::ImageViewMinLodParams minLodParams = { sampleParams.baseLevel, // int baseLevel; { sampleParams.imageViewMinLod, // float minLod; sampleParams.imageViewMinLodMode, // ImageViewMinLodMode }, sampleParams.samplerType == SAMPLERTYPE_FETCH_FLOAT // bool intTexCoord; }; const tcu::TextureCubeArrayView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel, sampleParams.imageViewMinLod != 0.0f ? &minLodParams : DE_NULL), srcLevelStorage, sampleParams.sampler); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[4 + 0], texCoord[8 + 0], texCoord[12 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[4 + 1], texCoord[8 + 1], texCoord[12 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[4 + 2], texCoord[8 + 2], texCoord[12 + 2]); const tcu::Vec4 qq = tcu::Vec4(texCoord[0 + 3], texCoord[4 + 3], texCoord[8 + 3], texCoord[12 + 3]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const int srcSize = src.getSize(); // Coordinates per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triQ[2] = {qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); const float posEps = 1.0f / float((1 << 4) + 1); // ES3 requires at least 4 subpixel bits. int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), // \note Not strictly allowed by spec, but implementations do this in practice. tcu::Vec2(-1, -1), tcu::Vec2(-1, +1), tcu::Vec2(+1, -1), tcu::Vec2(+1, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { // Ugly hack, validation can take way too long at the moment. if (watchDog) qpWatchDog_touch(watchDog); for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; // Try comparison to ideal reference first, and if that fails use slower verificator. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const bool tri0 = nx + ny - posEps <= 1.0f; const bool tri1 = nx + ny + posEps >= 1.0f; bool isOk = false; DE_ASSERT(tri0 || tri1); // Pixel can belong to either of the triangles if it lies close enough to the edge. for (int triNdx = (tri0 ? 0 : 1); triNdx <= (tri1 ? 1 : 0); triNdx++) { const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec4 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triQ[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec3 coordDx(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); const tcu::Vec3 coordDy(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord.toWidth<3>(), coordDx, coordDy, srcSize, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec3 coordO(projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); const tcu::Vec3 coordDxo(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); const tcu::Vec3 coordDyo(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coordBits, coord, clampedLod, resPix)) { isOk = true; break; } } if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } bool verifyTextureResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result, const tcu::TextureCubeArrayView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::LookupPrecision &lookupPrec, const tcu::IVec4 &coordBits, const tcu::LodPrecision &lodPrec, const tcu::PixelFormat &pixelFormat) { tcu::TestLog &log = testCtx.getLog(); tcu::Surface reference(result.getWidth(), result.getHeight()); tcu::Surface errorMask(result.getWidth(), result.getHeight()); int numFailedPixels; DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, coordBits, lodPrec, testCtx.getWatchDog()); if (numFailedPixels > 0) log << tcu::TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << tcu::TestLog::EndMessage; log << tcu::TestLog::ImageSet("VerifyResult", "Verification result") << tcu::TestLog::Image("Rendered", "Rendered image", result); if (numFailedPixels > 0) { log << tcu::TestLog::Image("Reference", "Ideal reference image", reference) << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); } log << tcu::TestLog::EndImageSet; return numFailedPixels == 0; } // Shadow lookup verification int computeTextureCompareDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture2DView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec, const tcu::Vec3 &nonShadowThreshold) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); const float minLod = (sampleParams.imageViewMinLod != 0.0f) ? de::max(deFloatFloor(sampleParams.imageViewMinLod), sampleParams.minLod) : sampleParams.minLod; int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = result.getPixel(px, py); const tcu::Vec4 refPix = reference.getPixel(px, py); // Other channels should trivially match to reference. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1, 2, 3) - resPix.swizzle(1, 2, 3)), nonShadowThreshold))) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; continue; } // Reference result is known to be a valid result, we can // skip verification if thes results are equal if (resPix.x() != refPix.x()) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec2 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } int computeTextureCompareDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::TextureCubeView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec, const tcu::Vec3 &nonShadowThreshold) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const int srcSize = src.getSize(); // Coordinates per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); const float minLod = (sampleParams.imageViewMinLod != 0.0f) ? de::max(deFloatFloor(sampleParams.imageViewMinLod), sampleParams.minLod) : sampleParams.minLod; int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = result.getPixel(px, py); const tcu::Vec4 refPix = reference.getPixel(px, py); // Other channels should trivially match to reference. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1, 2, 3) - resPix.swizzle(1, 2, 3)), nonShadowThreshold))) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; continue; } // Reference result is known to be a valid result, we can // skip verification if thes results are equal if (resPix.x() != refPix.x()) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec3 coordDx(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); const tcu::Vec3 coordDy(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord, coordDx, coordDy, srcSize, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec3 coordO(projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); const tcu::Vec3 coordDxo(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); const tcu::Vec3 coordDyo(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } int computeTextureCompareDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture2DArrayView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec, const tcu::Vec3 &nonShadowThreshold) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[3 + 0], texCoord[6 + 0], texCoord[9 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[3 + 1], texCoord[6 + 1], texCoord[9 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[3 + 2], texCoord[6 + 2], texCoord[9 + 2]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = result.getPixel(px, py); const tcu::Vec4 refPix = reference.getPixel(px, py); // Other channels should trivially match to reference. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1, 2, 3) - resPix.swizzle(1, 2, 3)), nonShadowThreshold))) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; continue; } // Reference result is known to be a valid result, we can // skip verification if thes results are equal if (resPix.x() != refPix.x()) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec3 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } int computeTextureCompareDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture1DView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec, const tcu::Vec3 &nonShadowThreshold) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); DE_ASSERT(result.getHeight() == 1); const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), 1); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const float srcSize = float(src.getWidth()); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); const int py = 0; { for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = result.getPixel(px, py); const tcu::Vec4 refPix = reference.getPixel(px, py); // Other channels should trivially match to reference. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1, 2, 3) - resPix.swizzle(1, 2, 3)), nonShadowThreshold))) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; continue; } // Reference result is known to be a valid result, we can // skip verification if thes results are equal if (resPix.x() != refPix.x()) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const float coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy)); const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * srcSize; tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDx, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * srcSize; const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * srcSize; const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, tcu::Vec1(coord), clampedLod, sampleParams.ref, resPix.x()); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } int computeTextureCompareDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::Texture1DArrayView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec, const tcu::Vec3 &nonShadowThreshold) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); DE_ASSERT(result.getHeight() == 1); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[2 + 0], texCoord[4 + 0], texCoord[6 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[2 + 1], texCoord[4 + 1], texCoord[6 + 1]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), 1); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const float srcSize = float(src.getWidth()); // Coordinates and lod per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); const int py = 0; { for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = result.getPixel(px, py); const tcu::Vec4 refPix = reference.getPixel(px, py); // Other channels should trivially match to reference. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1, 2, 3) - resPix.swizzle(1, 2, 3)), nonShadowThreshold))) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; continue; } // Reference result is known to be a valid result, we can // skip verification if these results are equal if (resPix.x() != refPix.x()) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec2 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * srcSize; tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDx, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * srcSize; const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * srcSize; const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } int computeTextureCompareDiff(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference, const tcu::PixelBufferAccess &errorMask, const tcu::TextureCubeArrayView &src, const float *texCoord, const ReferenceParams &sampleParams, const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec, const tcu::Vec3 &nonShadowThreshold) { DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); const tcu::Vec4 sq = tcu::Vec4(texCoord[0 + 0], texCoord[4 + 0], texCoord[8 + 0], texCoord[12 + 0]); const tcu::Vec4 tq = tcu::Vec4(texCoord[0 + 1], texCoord[4 + 1], texCoord[8 + 1], texCoord[12 + 1]); const tcu::Vec4 rq = tcu::Vec4(texCoord[0 + 2], texCoord[4 + 2], texCoord[8 + 2], texCoord[12 + 2]); const tcu::Vec4 qq = tcu::Vec4(texCoord[0 + 3], texCoord[4 + 3], texCoord[8 + 3], texCoord[12 + 3]); const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); const float dstW = float(dstSize.x()); const float dstH = float(dstSize.y()); const int srcSize = src.getSize(); // Coordinates per triangle. const tcu::Vec3 triS[2] = {sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1)}; const tcu::Vec3 triT[2] = {tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1)}; const tcu::Vec3 triR[2] = {rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1)}; const tcu::Vec3 triQ[2] = {qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1)}; const tcu::Vec3 triW[2] = {sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1)}; const tcu::Vec2 lodBias((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); int numFailed = 0; const tcu::Vec2 lodOffsets[] = { tcu::Vec2(-1, 0), tcu::Vec2(+1, 0), tcu::Vec2(0, -1), tcu::Vec2(0, +1), }; tcu::clear(errorMask, tcu::RGBA::green().toVec()); for (int py = 0; py < result.getHeight(); py++) { for (int px = 0; px < result.getWidth(); px++) { const tcu::Vec4 resPix = result.getPixel(px, py); const tcu::Vec4 refPix = reference.getPixel(px, py); // Other channels should trivially match to reference. if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1, 2, 3) - resPix.swizzle(1, 2, 3)), nonShadowThreshold))) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; continue; } // Reference result is known to be a valid result, we can // skip verification if thes results are equal if (resPix.x() != refPix.x()) { const float wx = (float)px + 0.5f; const float wy = (float)py + 0.5f; const float nx = wx / dstW; const float ny = wy / dstH; const int triNdx = nx + ny >= 1.0f ? 1 : 0; const float triWx = triNdx ? dstW - wx : wx; const float triWy = triNdx ? dstH - wy : wy; const float triNx = triNdx ? 1.0f - nx : nx; const float triNy = triNdx ? 1.0f - ny : ny; const tcu::Vec4 coord(projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy), projectedTriInterpolate(triQ[triNdx], triW[triNdx], triNx, triNy)); const tcu::Vec3 coordDx(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); const tcu::Vec3 coordDy(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord.swizzle(0, 1, 2), coordDx, coordDy, srcSize, lodPrec); // Compute lod bounds across lodOffsets range. for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) { const float wxo = triWx + lodOffsets[lodOffsNdx].x(); const float wyo = triWy + lodOffsets[lodOffsNdx].y(); const float nxo = wxo / dstW; const float nyo = wyo / dstH; const tcu::Vec3 coordO(projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); const tcu::Vec3 coordDxo(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); const tcu::Vec3 coordDyo(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); lodBounds.x() = de::min(lodBounds.x(), lodO.x()); lodBounds.y() = de::max(lodBounds.y(), lodO.y()); } const tcu::Vec2 clampedLod = tcu::clampLodBounds( lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); if (!isOk) { errorMask.setPixel(tcu::RGBA::red().toVec(), px, py); numFailed += 1; } } } } return numFailed; } // Mipmap generation comparison. static int compareGenMipmapBilinear(const tcu::ConstPixelBufferAccess &dst, const tcu::ConstPixelBufferAccess &src, const tcu::PixelBufferAccess &errorMask, const GenMipmapPrecision &precision) { DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures. const float dstW = float(dst.getWidth()); const float dstH = float(dst.getHeight()); const float srcW = float(src.getWidth()); const float srcH = float(src.getHeight()); int numFailed = 0; // Translation to lookup verification parameters. const tcu::Sampler sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, false /* non-normalized coords */); tcu::LookupPrecision lookupPrec; lookupPrec.colorThreshold = precision.colorThreshold; lookupPrec.colorMask = precision.colorMask; lookupPrec.coordBits = tcu::IVec3(22); lookupPrec.uvwBits = precision.filterBits; for (int y = 0; y < dst.getHeight(); y++) for (int x = 0; x < dst.getWidth(); x++) { const tcu::Vec4 result = dst.getPixel(x, y); const float cx = (float(x) + 0.5f) / dstW * srcW; const float cy = (float(y) + 0.5f) / dstH * srcH; const bool isOk = tcu::isLinearSampleResultValid(src, sampler, lookupPrec, tcu::Vec2(cx, cy), 0, result); errorMask.setPixel(isOk ? tcu::RGBA::green().toVec() : tcu::RGBA::red().toVec(), x, y); if (!isOk) numFailed += 1; } return numFailed; } static int compareGenMipmapBox(const tcu::ConstPixelBufferAccess &dst, const tcu::ConstPixelBufferAccess &src, const tcu::PixelBufferAccess &errorMask, const GenMipmapPrecision &precision) { DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures. const float dstW = float(dst.getWidth()); const float dstH = float(dst.getHeight()); const float srcW = float(src.getWidth()); const float srcH = float(src.getHeight()); int numFailed = 0; // Translation to lookup verification parameters. const tcu::Sampler sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, false /* non-normalized coords */); tcu::LookupPrecision lookupPrec; lookupPrec.colorThreshold = precision.colorThreshold; lookupPrec.colorMask = precision.colorMask; lookupPrec.coordBits = tcu::IVec3(22); lookupPrec.uvwBits = precision.filterBits; for (int y = 0; y < dst.getHeight(); y++) for (int x = 0; x < dst.getWidth(); x++) { const tcu::Vec4 result = dst.getPixel(x, y); const float cx = deFloatFloor(float(x) / dstW * srcW) + 1.0f; const float cy = deFloatFloor(float(y) / dstH * srcH) + 1.0f; const bool isOk = tcu::isLinearSampleResultValid(src, sampler, lookupPrec, tcu::Vec2(cx, cy), 0, result); errorMask.setPixel(isOk ? tcu::RGBA::green().toVec() : tcu::RGBA::red().toVec(), x, y); if (!isOk) numFailed += 1; } return numFailed; } static int compareGenMipmapVeryLenient(const tcu::ConstPixelBufferAccess &dst, const tcu::ConstPixelBufferAccess &src, const tcu::PixelBufferAccess &errorMask, const GenMipmapPrecision &precision) { DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures. DE_UNREF(precision); const float dstW = float(dst.getWidth()); const float dstH = float(dst.getHeight()); const float srcW = float(src.getWidth()); const float srcH = float(src.getHeight()); int numFailed = 0; for (int y = 0; y < dst.getHeight(); y++) for (int x = 0; x < dst.getWidth(); x++) { const tcu::Vec4 result = dst.getPixel(x, y); const int minX = deFloorFloatToInt32(((float)x - 0.5f) / dstW * srcW); const int minY = deFloorFloatToInt32(((float)y - 0.5f) / dstH * srcH); const int maxX = deCeilFloatToInt32(((float)x + 1.5f) / dstW * srcW); const int maxY = deCeilFloatToInt32(((float)y + 1.5f) / dstH * srcH); tcu::Vec4 minVal, maxVal; bool isOk; DE_ASSERT(minX < maxX && minY < maxY); for (int ky = minY; ky <= maxY; ky++) { for (int kx = minX; kx <= maxX; kx++) { const int sx = de::clamp(kx, 0, src.getWidth() - 1); const int sy = de::clamp(ky, 0, src.getHeight() - 1); const tcu::Vec4 sample = src.getPixel(sx, sy); if (ky == minY && kx == minX) { minVal = sample; maxVal = sample; } else { minVal = min(sample, minVal); maxVal = max(sample, maxVal); } } } isOk = boolAll(logicalAnd(lessThanEqual(minVal, result), lessThanEqual(result, maxVal))); errorMask.setPixel(isOk ? tcu::RGBA::green().toVec() : tcu::RGBA::red().toVec(), x, y); if (!isOk) numFailed += 1; } return numFailed; } qpTestResult compareGenMipmapResult(tcu::TestLog &log, const tcu::Texture2D &resultTexture, const tcu::Texture2D &level0Reference, const GenMipmapPrecision &precision) { qpTestResult result = QP_TEST_RESULT_PASS; // Special comparison for level 0. { const tcu::Vec4 threshold = select(precision.colorThreshold, tcu::Vec4(1.0f), precision.colorMask); const bool level0Ok = tcu::floatThresholdCompare(log, "Level0", "Level 0", level0Reference.getLevel(0), resultTexture.getLevel(0), threshold, tcu::COMPARE_LOG_RESULT); if (!level0Ok) { log << tcu::TestLog::Message << "ERROR: Level 0 comparison failed!" << tcu::TestLog::EndMessage; result = QP_TEST_RESULT_FAIL; } } for (int levelNdx = 1; levelNdx < resultTexture.getNumLevels(); levelNdx++) { const tcu::ConstPixelBufferAccess src = resultTexture.getLevel(levelNdx - 1); const tcu::ConstPixelBufferAccess dst = resultTexture.getLevel(levelNdx); tcu::Surface errorMask(dst.getWidth(), dst.getHeight()); bool levelOk = false; // Try different comparisons in quality order. if (!levelOk) { const int numFailed = compareGenMipmapBilinear(dst, src, errorMask.getAccess(), precision); if (numFailed == 0) levelOk = true; else log << tcu::TestLog::Message << "WARNING: Level " << levelNdx << " comparison to bilinear method failed, found " << numFailed << " invalid pixels." << tcu::TestLog::EndMessage; } if (!levelOk) { const int numFailed = compareGenMipmapBox(dst, src, errorMask.getAccess(), precision); if (numFailed == 0) levelOk = true; else log << tcu::TestLog::Message << "WARNING: Level " << levelNdx << " comparison to box method failed, found " << numFailed << " invalid pixels." << tcu::TestLog::EndMessage; } // At this point all high-quality methods have been used. if (!levelOk && result == QP_TEST_RESULT_PASS) result = QP_TEST_RESULT_QUALITY_WARNING; if (!levelOk) { const int numFailed = compareGenMipmapVeryLenient(dst, src, errorMask.getAccess(), precision); if (numFailed == 0) levelOk = true; else log << tcu::TestLog::Message << "ERROR: Level " << levelNdx << " appears to contain " << numFailed << " completely wrong pixels, failing case!" << tcu::TestLog::EndMessage; } if (!levelOk) result = QP_TEST_RESULT_FAIL; log << tcu::TestLog::ImageSet(string("Level") + de::toString(levelNdx), string("Level ") + de::toString(levelNdx) + " result") << tcu::TestLog::Image("Result", "Result", dst); if (!levelOk) log << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); log << tcu::TestLog::EndImageSet; } return result; } qpTestResult compareGenMipmapResult(tcu::TestLog &log, const tcu::TextureCube &resultTexture, const tcu::TextureCube &level0Reference, const GenMipmapPrecision &precision) { qpTestResult result = QP_TEST_RESULT_PASS; static const char *s_faceNames[] = {"-X", "+X", "-Y", "+Y", "-Z", "+Z"}; DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_faceNames) == tcu::CUBEFACE_LAST); // Special comparison for level 0. for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) { const tcu::CubeFace face = tcu::CubeFace(faceNdx); const tcu::Vec4 threshold = select(precision.colorThreshold, tcu::Vec4(1.0f), precision.colorMask); const bool level0Ok = tcu::floatThresholdCompare( log, ("Level0Face" + de::toString(faceNdx)).c_str(), (string("Level 0, face ") + s_faceNames[face]).c_str(), level0Reference.getLevelFace(0, face), resultTexture.getLevelFace(0, face), threshold, tcu::COMPARE_LOG_RESULT); if (!level0Ok) { log << tcu::TestLog::Message << "ERROR: Level 0, face " << s_faceNames[face] << " comparison failed!" << tcu::TestLog::EndMessage; result = QP_TEST_RESULT_FAIL; } } for (int levelNdx = 1; levelNdx < resultTexture.getNumLevels(); levelNdx++) { for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) { const tcu::CubeFace face = tcu::CubeFace(faceNdx); const char *faceName = s_faceNames[face]; const tcu::ConstPixelBufferAccess src = resultTexture.getLevelFace(levelNdx - 1, face); const tcu::ConstPixelBufferAccess dst = resultTexture.getLevelFace(levelNdx, face); tcu::Surface errorMask(dst.getWidth(), dst.getHeight()); bool levelOk = false; // Try different comparisons in quality order. if (!levelOk) { const int numFailed = compareGenMipmapBilinear(dst, src, errorMask.getAccess(), precision); if (numFailed == 0) levelOk = true; else log << tcu::TestLog::Message << "WARNING: Level " << levelNdx << ", face " << faceName << " comparison to bilinear method failed, found " << numFailed << " invalid pixels." << tcu::TestLog::EndMessage; } if (!levelOk) { const int numFailed = compareGenMipmapBox(dst, src, errorMask.getAccess(), precision); if (numFailed == 0) levelOk = true; else log << tcu::TestLog::Message << "WARNING: Level " << levelNdx << ", face " << faceName << " comparison to box method failed, found " << numFailed << " invalid pixels." << tcu::TestLog::EndMessage; } // At this point all high-quality methods have been used. if (!levelOk && result == QP_TEST_RESULT_PASS) result = QP_TEST_RESULT_QUALITY_WARNING; if (!levelOk) { const int numFailed = compareGenMipmapVeryLenient(dst, src, errorMask.getAccess(), precision); if (numFailed == 0) levelOk = true; else log << tcu::TestLog::Message << "ERROR: Level " << levelNdx << ", face " << faceName << " appears to contain " << numFailed << " completely wrong pixels, failing case!" << tcu::TestLog::EndMessage; } if (!levelOk) result = QP_TEST_RESULT_FAIL; log << tcu::TestLog::ImageSet(string("Level") + de::toString(levelNdx) + "Face" + de::toString(faceNdx), string("Level ") + de::toString(levelNdx) + ", face " + string(faceName) + " result") << tcu::TestLog::Image("Result", "Result", dst); if (!levelOk) log << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask); log << tcu::TestLog::EndImageSet; } } return result; } // Logging utilities. std::ostream &operator<<(std::ostream &str, const LogGradientFmt &fmt) { return str << "(R: " << fmt.valueMin->x() << " -> " << fmt.valueMax->x() << ", " << "G: " << fmt.valueMin->y() << " -> " << fmt.valueMax->y() << ", " << "B: " << fmt.valueMin->z() << " -> " << fmt.valueMax->z() << ", " << "A: " << fmt.valueMin->w() << " -> " << fmt.valueMax->w() << ")"; } } // namespace TextureTestUtil } // namespace glu