--- /dev/null
+/*-------------------------------------------------------------------------
+ * OpenGL Conformance Test Suite
+ * -----------------------------
+ *
+ * Copyright (c) 2014-2019 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */ /*!
+ * \file
+ * \brief
+ */ /*-------------------------------------------------------------------*/
+
+/*!
+ * \file esextcTextureShadowLodFunctionsTest.cpp
+ * \brief EXT_texture_shadow_lod extension testing
+ */ /*-------------------------------------------------------------------*/
+
+#include "esextcTextureShadowLodFunctionsTest.hpp"
+#include "deMath.h"
+#include "glcShaderLibrary.hpp"
+#include "glcShaderRenderCase.hpp"
+#include "glsTextureTestUtil.hpp"
+#include "gluPixelTransfer.hpp"
+#include "gluStrUtil.hpp"
+#include "gluTexture.hpp"
+#include "gluTextureUtil.hpp"
+#include "glwFunctions.hpp"
+#include "tcuMatrix.hpp"
+#include "tcuMatrixUtil.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuTextureUtil.hpp"
+
+#include <sstream>
+
+#include "glwEnums.hpp"
+#include "glwFunctions.hpp"
+
+namespace deqp
+{
+namespace Functional
+{
+namespace
+{
+
+using glu::TextureTestUtil::computeLodFromDerivates;
+
+enum Function
+{
+ FUNCTION_TEXTURE = 0, //!< texture(), textureOffset()
+ FUNCTION_TEXTURELOD, // ...
+ FUNCTION_LAST
+};
+
+// For texture(..., [bias]) functions.
+inline bool functionHasAutoLod(glu::ShaderType shaderType, Function function)
+{
+ return (shaderType == glu::SHADERTYPE_FRAGMENT) && (function == FUNCTION_TEXTURE);
+}
+
+// For textureLod* functions.
+inline bool functionHasLod(Function function)
+{
+ return function == FUNCTION_TEXTURELOD;
+}
+
+struct TextureLookupSpec
+{
+ // The texture function to use.
+ Function function;
+
+ // Min/max texture coordinates.
+ tcu::Vec4 minCoord;
+ tcu::Vec4 maxCoord;
+
+ // Bias
+ bool useBias;
+
+ // Bias or Lod for *Lod* functions
+ float minLodBias;
+ float maxLodBias;
+
+ // For *Offset variants
+ bool useOffset;
+ tcu::IVec3 offset;
+
+ // Do we require an additional shadow ref "compare"
+ // parameter in the texture function's parameter list? (used for
+ // shadow cube array textures).
+ bool useSepRef;
+ float minSepRef;
+ float maxSepRef;
+
+ TextureLookupSpec(void)
+ : function(FUNCTION_LAST)
+ , minCoord(0.0f)
+ , maxCoord(1.0f)
+ , useBias(false)
+ , minLodBias(0.0f)
+ , maxLodBias(0.0f)
+ , useOffset(false)
+ , offset(0)
+ , useSepRef(false)
+ , minSepRef(0.0f)
+ , maxSepRef(0.0f)
+ {
+ }
+
+ TextureLookupSpec(Function function_, const tcu::Vec4& minCoord_, const tcu::Vec4& maxCoord_, bool useBias_,
+ float minLodBias_, float maxLodBias_, bool useOffset_, const tcu::IVec3& offset_, bool useSepRef_,
+ float minSepRef_, float maxSepRef_)
+ : function(function_)
+ , minCoord(minCoord_)
+ , maxCoord(maxCoord_)
+ , useBias(useBias_)
+ , minLodBias(minLodBias_)
+ , maxLodBias(maxLodBias_)
+ , useOffset(useOffset_)
+ , offset(offset_)
+ , useSepRef(useSepRef_)
+ , minSepRef(minSepRef_)
+ , maxSepRef(maxSepRef_)
+ {
+ }
+};
+
+// Only shadow texture types contained in EXT_texture_shadow_lod will be tested.
+enum TextureType
+{
+ TEXTURETYPE_2D,
+ TEXTURETYPE_CUBE_MAP,
+ TEXTURETYPE_CUBE_MAP_ARRAY,
+ TEXTURETYPE_2D_ARRAY,
+
+ TEXTURETYPE_LAST
+};
+
+struct TextureSpec
+{
+ TextureType type; //!< Texture type (2D, cubemap, ...)
+ deUint32 format; //!< Internal format.
+ int width;
+ int height;
+ int depth;
+ int numLevels;
+ tcu::Sampler sampler;
+
+ TextureSpec(void) : type(TEXTURETYPE_LAST), format(GL_NONE), width(0), height(0), depth(0), numLevels(0)
+ {
+ }
+
+ TextureSpec(TextureType type_, deUint32 format_, int width_, int height_, int depth_, int numLevels_,
+ const tcu::Sampler& sampler_)
+ : type(type_)
+ , format(format_)
+ , width(width_)
+ , height(height_)
+ , depth(depth_)
+ , numLevels(numLevels_)
+ , sampler(sampler_)
+ {
+ }
+};
+
+struct TexLookupParams
+{
+ float lod;
+ tcu::IVec3 offset;
+ tcu::Vec4 scale;
+ tcu::Vec4 bias;
+
+ TexLookupParams(void) : lod(0.0f), offset(0), scale(1.0f), bias(0.0f)
+ {
+ }
+};
+
+} // namespace
+
+using tcu::IVec2;
+using tcu::IVec3;
+using tcu::IVec4;
+using tcu::Vec2;
+using tcu::Vec3;
+using tcu::Vec4;
+
+static const glu::TextureTestUtil::LodMode DEFAULT_LOD_MODE = glu::TextureTestUtil::LODMODE_EXACT;
+
+typedef void (*TexEvalFunc)(ShaderEvalContext& c, const TexLookupParams& lookupParams);
+
+inline float texture2DShadow(const ShaderEvalContext& c, float ref, float s, float t, float lod)
+{
+ return c.textures[0].tex2D->sampleCompare(c.textures[0].sampler, ref, s, t, lod);
+}
+inline float texture2DArrayShadow(const ShaderEvalContext& c, float ref, float s, float t, float r, float lod)
+{
+ return c.textures[0].tex2DArray->sampleCompare(c.textures[0].sampler, ref, s, t, r, lod);
+}
+inline float textureCubeShadow(const ShaderEvalContext& c, float ref, float s, float t, float r, float lod)
+{
+ return c.textures[0].texCube->sampleCompare(c.textures[0].sampler, ref, s, t, r, lod);
+}
+inline float textureCubeArrayShadow(const ShaderEvalContext& c, float ref, float s, float t, float r, float q,
+ float lod)
+{
+ return c.textures[0].texCubeArray->sampleCompare(c.textures[0].sampler, ref, s, t, r, q, lod);
+}
+inline float texture2DShadowOffset(const ShaderEvalContext& c, float ref, float s, float t, float lod, IVec2 offset)
+{
+ return c.textures[0].tex2D->sampleCompareOffset(c.textures[0].sampler, ref, s, t, lod, offset);
+}
+inline float texture2DArrayShadowOffset(const ShaderEvalContext& c, float ref, float s, float t, float r, float lod,
+ IVec2 offset)
+{
+ return c.textures[0].tex2DArray->sampleCompareOffset(c.textures[0].sampler, ref, s, t, r, lod, offset);
+}
+
+// Shadow evaluation functions
+static void evalTexture2DArrayShadow(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = texture2DArrayShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod);
+}
+static void evalTexture2DArrayShadowBias(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = texture2DArrayShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod + c.in[1].x());
+}
+static void evalTexture2DArrayShadowOffset(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = texture2DArrayShadowOffset(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod,
+ p.offset.swizzle(0, 1));
+}
+static void evalTexture2DArrayShadowOffsetBias(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = texture2DArrayShadowOffset(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod + c.in[1].x(),
+ p.offset.swizzle(0, 1));
+}
+
+static void evalTexture2DArrayShadowLod(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ (void)p;
+ c.color.x() = texture2DArrayShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[1].x());
+}
+static void evalTexture2DArrayShadowLodOffset(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = texture2DArrayShadowOffset(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[1].x(),
+ p.offset.swizzle(0, 1));
+}
+
+static void evalTextureCubeShadow(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = textureCubeShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod);
+}
+static void evalTextureCubeShadowBias(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = textureCubeShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod + c.in[1].x());
+}
+static void evalTextureCubeShadowLod(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ (void)p;
+ c.color.x() = textureCubeShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[1].x());
+}
+
+static void evalTextureCubeArrayShadow(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() = textureCubeArrayShadow(c, c.in[1].y(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[0].w(), p.lod);
+}
+static void evalTextureCubeArrayShadowBias(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ c.color.x() =
+ textureCubeArrayShadow(c, c.in[1].y(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[0].w(), p.lod + c.in[1].x());
+}
+static void evalTextureCubeArrayShadowLod(ShaderEvalContext& c, const TexLookupParams& p)
+{
+ (void)p;
+ c.color.x() =
+ textureCubeArrayShadow(c, c.in[1].y(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[0].w(), c.in[1].x());
+}
+
+class TexLookupEvaluator : public ShaderEvaluator
+{
+public:
+ TexLookupEvaluator(TexEvalFunc evalFunc, const TexLookupParams& lookupParams)
+ : m_evalFunc(evalFunc), m_lookupParams(lookupParams)
+ {
+ }
+
+ virtual void evaluate(ShaderEvalContext& ctx)
+ {
+ m_evalFunc(ctx, m_lookupParams);
+ }
+
+private:
+ TexEvalFunc m_evalFunc;
+ const TexLookupParams& m_lookupParams;
+};
+
+class TextureShadowLodTestCase : public ShaderRenderCase
+{
+public:
+ TextureShadowLodTestCase(Context& context, const char* name, const char* desc, const TextureLookupSpec& lookup,
+ const TextureSpec& texture, TexEvalFunc evalFunc, bool isVertexCase);
+ ~TextureShadowLodTestCase(void);
+
+ void init(void);
+ void deinit(void);
+
+protected:
+ void setupUniforms(deUint32 programID, const tcu::Vec4& constCoords);
+
+private:
+ void initTexture(void);
+ void initShaderSources(void);
+
+ TextureLookupSpec m_lookupSpec;
+ TextureSpec m_textureSpec;
+
+ TexLookupParams m_lookupParams;
+ TexLookupEvaluator m_evaluator;
+
+ glu::Texture2D* m_texture2D;
+ glu::TextureCube* m_textureCube;
+ glu::TextureCubeArray* m_textureCubeArray;
+ glu::Texture2DArray* m_texture2DArray;
+};
+
+TextureShadowLodTestCase::TextureShadowLodTestCase(Context& context, const char* name, const char* desc,
+ const TextureLookupSpec& lookup, const TextureSpec& texture,
+ TexEvalFunc evalFunc, bool isVertexCase)
+ : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc,
+ isVertexCase, m_evaluator)
+ , m_lookupSpec(lookup)
+ , m_textureSpec(texture)
+ , m_evaluator(evalFunc, m_lookupParams)
+ , m_texture2D(DE_NULL)
+ , m_textureCube(DE_NULL)
+ , m_textureCubeArray(DE_NULL)
+ , m_texture2DArray(DE_NULL)
+{
+}
+
+TextureShadowLodTestCase::~TextureShadowLodTestCase(void)
+{
+ delete m_texture2D;
+ delete m_textureCube;
+ delete m_textureCubeArray;
+ delete m_texture2DArray;
+}
+
+void TextureShadowLodTestCase::init(void)
+{
+ // Check extension and other features are supported with this context/platform.
+ {
+ glu::ContextInfo* info = glu::ContextInfo::create(m_renderCtx);
+
+ // First check if extension is available.
+ if (!info->isExtensionSupported("GL_EXT_texture_shadow_lod"))
+ {
+ throw tcu::NotSupportedError("EXT_texture_shadow_lod is not supported on the platform");
+ }
+
+ // Check that API support and that the various texture_cube_map_array extension is supported based on GL / ES versions.
+
+ if (glu::isContextTypeES(m_renderCtx.getType()))
+ {
+ // ES
+ if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 0)))
+ {
+ throw tcu::NotSupportedError(
+ "EXT_texture_shadow_lod is not supported due to minimum ES version requirements");
+ }
+
+ // Check if cube map array is supported. For ES, it is supported
+ // as of ES 3.2, or with OES_texture_cube_map_array for
+ // 3.1.
+ if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
+ {
+ if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) &&
+ !(glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 1)) &&
+ (info->isExtensionSupported("GL_OES_texture_cube_map_array") ||
+ info->isExtensionSupported("GL_EXT_texture_cube_map_array"))))
+ {
+ throw tcu::NotSupportedError("GL_OES_texture_cube_map_array or GL_EXT_texture_cube_map_array is "
+ "required for this configuration and is not available.");
+ }
+ }
+ }
+ else
+ {
+ // GL
+ if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(2, 0)))
+ {
+ throw tcu::NotSupportedError(
+ "EXT_texture_shadow_lod is not supported due to minimum GL version requirements");
+ }
+
+ // Check if cube map array is supported. For GL, it is supported
+ // as of GL 4.0 core, or with ARB_texture_cube_map_array prior.
+ if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
+ {
+ if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 0)) &&
+ !info->isExtensionSupported("GL_ARB_texture_cube_map_array"))
+ {
+ throw tcu::NotSupportedError(
+ "ARB_texture_cube_map_array is required for this configuration and is not available.");
+ }
+ }
+ }
+ }
+
+ // Set up the user attributes.
+ // The user attributes are set up as matrices where each row represents a component
+ // in the attribute transformed against the vertex's interpolated position across the grid.
+ {
+ // Base coord scale & bias
+ Vec4 s = m_lookupSpec.maxCoord - m_lookupSpec.minCoord;
+ Vec4 b = m_lookupSpec.minCoord;
+
+ float baseCoordTrans[] = { s.x(), 0.0f, 0.f, b.x(),
+ 0.f, s.y(), 0.f, b.y(),
+ s.z() / 2.f, -s.z() / 2.f, 0.f, s.z() / 2.f + b.z(),
+ -s.w() / 2.f, s.w() / 2.f, 0.f, s.w() / 2.f + b.w() };
+
+ //a_in0
+ m_userAttribTransforms.push_back(tcu::Mat4(baseCoordTrans));
+ }
+
+ bool hasLodBias = functionHasLod(m_lookupSpec.function) || m_lookupSpec.useBias;
+
+ if (hasLodBias || m_lookupSpec.useSepRef)
+ {
+ float s = 0.0f;
+ float b = 0.0f;
+ float sepRefS = 0.0f;
+ float sepRefB = 0.0f;
+
+ if (hasLodBias)
+ {
+ s = m_lookupSpec.maxLodBias - m_lookupSpec.minLodBias;
+ b = m_lookupSpec.minLodBias;
+ }
+
+ if (m_lookupSpec.useSepRef)
+ {
+ sepRefS = m_lookupSpec.maxSepRef - m_lookupSpec.minSepRef;
+ sepRefB = m_lookupSpec.minSepRef;
+ }
+ float lodCoordTrans[] = { s / 2.0f, s / 2.0f, 0.f, b, sepRefS / 2.0f, sepRefS / 2.0f, 0.0f, sepRefB,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
+ //a_in1
+ m_userAttribTransforms.push_back(tcu::Mat4(lodCoordTrans));
+ }
+
+ initShaderSources();
+ initTexture();
+
+ ShaderRenderCase::init();
+}
+
+void TextureShadowLodTestCase::initTexture(void)
+{
+ static const IVec4 texCubeSwz[] = { IVec4(0, 0, 1, 1), IVec4(1, 1, 0, 0), IVec4(0, 1, 0, 1),
+ IVec4(1, 0, 1, 0), IVec4(0, 1, 1, 0), IVec4(1, 0, 0, 1) };
+ DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(texCubeSwz) == tcu::CUBEFACE_LAST);
+
+ tcu::TextureFormat texFmt = glu::mapGLInternalFormat(m_textureSpec.format);
+ tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
+ tcu::IVec2 viewportSize = getViewportSize();
+ bool isAutoLod = functionHasAutoLod(m_isVertexCase ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT,
+ m_lookupSpec.function); // LOD can vary significantly
+
+ switch (m_textureSpec.type)
+ {
+ case TEXTURETYPE_2D:
+ {
+ float levelStep = isAutoLod ? 0.0f : 1.0f / (float)de::max(1, m_textureSpec.numLevels - 1);
+ Vec4 cScale = fmtInfo.valueMax - fmtInfo.valueMin;
+ Vec4 cBias = fmtInfo.valueMin;
+ int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
+
+ m_texture2D = new glu::Texture2D(m_renderCtx, m_textureSpec.format, m_textureSpec.width, m_textureSpec.height);
+ for (int level = 0; level < m_textureSpec.numLevels; level++)
+ {
+ float fA = float(level) * levelStep;
+ float fB = 1.0f - fA;
+ Vec4 colorA = cBias + cScale * Vec4(fA, fB, fA, fB);
+ Vec4 colorB = cBias + cScale * Vec4(fB, fA, fB, fA);
+
+ m_texture2D->getRefTexture().allocLevel(level);
+ tcu::fillWithGrid(m_texture2D->getRefTexture().getLevel(level), de::max(1, baseCellSize >> level), colorA,
+ colorB);
+ }
+ m_texture2D->upload();
+
+ // Compute LOD.
+ float dudx =
+ (m_lookupSpec.maxCoord[0] - m_lookupSpec.minCoord[0]) * (float)m_textureSpec.width / (float)viewportSize[0];
+ float dvdy = (m_lookupSpec.maxCoord[1] - m_lookupSpec.minCoord[1]) * (float)m_textureSpec.height /
+ (float)viewportSize[1];
+ m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
+
+ // Append to texture list.
+ m_textures.push_back(TextureBinding(m_texture2D, m_textureSpec.sampler));
+ break;
+ }
+
+ case TEXTURETYPE_2D_ARRAY:
+ {
+ float layerStep = 1.0f / (float)m_textureSpec.depth;
+ float levelStep =
+ isAutoLod ? 0.0f : 1.0f / (float)(de::max(1, m_textureSpec.numLevels - 1) * m_textureSpec.depth);
+ Vec4 cScale = fmtInfo.valueMax - fmtInfo.valueMin;
+ Vec4 cBias = fmtInfo.valueMin;
+ int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
+
+ m_texture2DArray = new glu::Texture2DArray(m_renderCtx, m_textureSpec.format, m_textureSpec.width,
+ m_textureSpec.height, m_textureSpec.depth);
+ for (int level = 0; level < m_textureSpec.numLevels; level++)
+ {
+ m_texture2DArray->getRefTexture().allocLevel(level);
+ tcu::PixelBufferAccess levelAccess = m_texture2DArray->getRefTexture().getLevel(level);
+
+ for (int layer = 0; layer < levelAccess.getDepth(); layer++)
+ {
+ float fA = (float)layer * layerStep + (float)level * levelStep;
+ float fB = 1.0f - fA;
+ Vec4 colorA = cBias + cScale * Vec4(fA, fB, fA, fB);
+ Vec4 colorB = cBias + cScale * Vec4(fB, fA, fB, fA);
+
+ tcu::fillWithGrid(
+ tcu::getSubregion(levelAccess, 0, 0, layer, levelAccess.getWidth(), levelAccess.getHeight(), 1),
+ de::max(1, baseCellSize >> level), colorA, colorB);
+ }
+ }
+ m_texture2DArray->upload();
+
+ // Compute LOD.
+ float dudx =
+ (m_lookupSpec.maxCoord[0] - m_lookupSpec.minCoord[0]) * (float)m_textureSpec.width / (float)viewportSize[0];
+ float dvdy = (m_lookupSpec.maxCoord[1] - m_lookupSpec.minCoord[1]) * (float)m_textureSpec.height /
+ (float)viewportSize[1];
+ m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
+
+ // Append to texture list.
+ m_textures.push_back(TextureBinding(m_texture2DArray, m_textureSpec.sampler));
+ break;
+ }
+
+ case TEXTURETYPE_CUBE_MAP:
+ {
+ float levelStep = isAutoLod ? 0.0f : 1.0f / (float)de::max(1, m_textureSpec.numLevels - 1);
+ Vec4 cScale = fmtInfo.valueMax - fmtInfo.valueMin;
+ Vec4 cBias = fmtInfo.valueMin;
+ Vec4 cCorner = cBias + cScale * 0.5f;
+ int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
+
+ DE_ASSERT(m_textureSpec.width == m_textureSpec.height);
+ m_textureCube = new glu::TextureCube(m_renderCtx, m_textureSpec.format, m_textureSpec.width);
+ for (int level = 0; level < m_textureSpec.numLevels; level++)
+ {
+ float fA = float(level) * levelStep;
+ float fB = 1.0f - fA;
+ Vec2 f(fA, fB);
+
+ for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
+ {
+ const IVec4& swzA = texCubeSwz[face];
+ IVec4 swzB = 1 - swzA;
+ Vec4 colorA = cBias + cScale * f.swizzle(swzA[0], swzA[1], swzA[2], swzA[3]);
+ Vec4 colorB = cBias + cScale * f.swizzle(swzB[0], swzB[1], swzB[2], swzB[3]);
+
+ m_textureCube->getRefTexture().allocLevel((tcu::CubeFace)face, level);
+ {
+ const tcu::PixelBufferAccess access =
+ m_textureCube->getRefTexture().getLevelFace(level, (tcu::CubeFace)face);
+ const int lastPix = access.getWidth() - 1;
+
+ tcu::fillWithGrid(access, de::max(1, baseCellSize >> level), colorA, colorB);
+
+ // Ensure all corners have identical colors in order to avoid dealing with ambiguous corner texel filtering
+ access.setPixel(cCorner, 0, 0);
+ access.setPixel(cCorner, 0, lastPix);
+ access.setPixel(cCorner, lastPix, 0);
+ access.setPixel(cCorner, lastPix, lastPix);
+ }
+ }
+ }
+ m_textureCube->upload();
+
+ // Compute LOD \note Assumes that only single side is accessed and R is constant major axis.
+ DE_ASSERT(de::abs(m_lookupSpec.minCoord[2] - m_lookupSpec.maxCoord[2]) < 0.005);
+ DE_ASSERT(de::abs(m_lookupSpec.minCoord[0]) < de::abs(m_lookupSpec.minCoord[2]) &&
+ de::abs(m_lookupSpec.maxCoord[0]) < de::abs(m_lookupSpec.minCoord[2]));
+ DE_ASSERT(de::abs(m_lookupSpec.minCoord[1]) < de::abs(m_lookupSpec.minCoord[2]) &&
+ de::abs(m_lookupSpec.maxCoord[1]) < de::abs(m_lookupSpec.minCoord[2]));
+
+ tcu::CubeFaceFloatCoords c00 =
+ tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
+ tcu::CubeFaceFloatCoords c10 =
+ tcu::getCubeFaceCoords(Vec3(m_lookupSpec.maxCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
+ tcu::CubeFaceFloatCoords c01 =
+ tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.maxCoord[1], m_lookupSpec.minCoord[2]));
+ float dudx = (c10.s - c00.s) * (float)m_textureSpec.width / (float)viewportSize[0];
+ float dvdy = (c01.t - c00.t) * (float)m_textureSpec.height / (float)viewportSize[1];
+
+ m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
+
+ m_textures.push_back(TextureBinding(m_textureCube, m_textureSpec.sampler));
+ break;
+ }
+
+ case TEXTURETYPE_CUBE_MAP_ARRAY:
+ {
+ float layerStep = 1.0f / (float)m_textureSpec.depth;
+ float levelStep =
+ isAutoLod ? 0.0f : 1.0f / (float)(de::max(1, m_textureSpec.numLevels - 1) * m_textureSpec.depth);
+ Vec4 cScale = fmtInfo.valueMax - fmtInfo.valueMin;
+ Vec4 cBias = fmtInfo.valueMin;
+ Vec4 cCorner = cBias + cScale * 0.5f;
+ int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
+
+ DE_ASSERT(m_textureSpec.width == m_textureSpec.height);
+ // I think size here means width/height of cube tex
+ m_textureCubeArray =
+ new glu::TextureCubeArray(m_renderCtx, m_textureSpec.format, m_textureSpec.width, m_textureSpec.depth * 6);
+ // mipmap level
+ for (int level = 0; level < m_textureSpec.numLevels; level++)
+ {
+ m_textureCubeArray->getRefTexture().allocLevel(level);
+ tcu::PixelBufferAccess levelAccess = m_textureCubeArray->getRefTexture().getLevel(level);
+
+ //array layer
+ DE_ASSERT((levelAccess.getDepth() % 6) == 0);
+
+ for (int layer = 0; layer < levelAccess.getDepth() / 6; ++layer)
+ {
+ for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
+ {
+ float fA = (float)layer * layerStep + float(level) * levelStep;
+ float fB = 1.0f - fA;
+ Vec2 f(fA, fB);
+
+ const IVec4& swzA = texCubeSwz[face];
+ IVec4 swzB = 1 - swzA;
+ Vec4 colorA = cBias + cScale * f.swizzle(swzA[0], swzA[1], swzA[2], swzA[3]);
+ Vec4 colorB = cBias + cScale * f.swizzle(swzB[0], swzB[1], swzB[2], swzB[3]);
+
+ {
+ const tcu::PixelBufferAccess access = m_textureCubeArray->getRefTexture().getLevel(level);
+ const int lastPix = access.getWidth() - 1;
+
+ int layerFaceNdx = face + layer * 6;
+
+ DE_ASSERT(levelAccess.getWidth() == levelAccess.getHeight());
+ tcu::fillWithGrid(tcu::getSubregion(access, 0, 0, layerFaceNdx, levelAccess.getWidth(),
+ levelAccess.getHeight(), 1),
+ de::max(1, baseCellSize >> level), colorA, colorB);
+
+ // Ensure all corners have identical colors in order to avoid dealing with ambiguous corner texel filtering
+ access.setPixel(cCorner, 0, 0, layer);
+ access.setPixel(cCorner, 0, lastPix, layer);
+ access.setPixel(cCorner, lastPix, 0, layer);
+ access.setPixel(cCorner, lastPix, lastPix, layer);
+ }
+ }
+ }
+ }
+ m_textureCubeArray->upload();
+
+ // Compute LOD \note Assumes that only single side is accessed and R is constant major axis.
+ DE_ASSERT(de::abs(m_lookupSpec.minCoord[2] - m_lookupSpec.maxCoord[2]) < 0.005);
+ DE_ASSERT(de::abs(m_lookupSpec.minCoord[0]) < de::abs(m_lookupSpec.minCoord[2]) &&
+ de::abs(m_lookupSpec.maxCoord[0]) < de::abs(m_lookupSpec.minCoord[2]));
+ DE_ASSERT(de::abs(m_lookupSpec.minCoord[1]) < de::abs(m_lookupSpec.minCoord[2]) &&
+ de::abs(m_lookupSpec.maxCoord[1]) < de::abs(m_lookupSpec.minCoord[2]));
+
+ tcu::CubeFaceFloatCoords c00 =
+ tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
+ tcu::CubeFaceFloatCoords c10 =
+ tcu::getCubeFaceCoords(Vec3(m_lookupSpec.maxCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
+ tcu::CubeFaceFloatCoords c01 =
+ tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.maxCoord[1], m_lookupSpec.minCoord[2]));
+ float dudx = (c10.s - c00.s) * (float)m_textureSpec.width / (float)viewportSize[0];
+ float dvdy = (c01.t - c00.t) * (float)m_textureSpec.height / (float)viewportSize[1];
+
+ m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
+
+ m_textures.push_back(TextureBinding(m_textureCubeArray, m_textureSpec.sampler));
+ break;
+ }
+
+ default:
+ DE_ASSERT(DE_FALSE);
+ }
+
+ // Set lookup scale & bias
+ m_lookupParams.scale = fmtInfo.lookupScale;
+ m_lookupParams.bias = fmtInfo.lookupBias;
+ m_lookupParams.offset = m_lookupSpec.offset;
+}
+
+void TextureShadowLodTestCase::initShaderSources(void)
+{
+ Function function = m_lookupSpec.function;
+ bool isVtxCase = m_isVertexCase;
+ bool hasLodBias = functionHasLod(m_lookupSpec.function) || m_lookupSpec.useBias;
+ int texCoordComps = m_textureSpec.type == TEXTURETYPE_2D ? 2 : 3;
+ int extraCoordComps = 1; // For shadow ref
+ bool hasSepShadowRef = m_lookupSpec.useSepRef;
+ glu::DataType coordType = glu::getDataTypeFloatVec(texCoordComps + extraCoordComps);
+ glu::Precision coordPrec = glu::PRECISION_HIGHP;
+ const char* coordTypeName = glu::getDataTypeName(coordType);
+ const char* coordPrecName = glu::getPrecisionName(coordPrec);
+ glu::DataType samplerType = glu::TYPE_LAST;
+ const char* baseFuncName = DE_NULL;
+
+ switch (m_textureSpec.type)
+ {
+ case TEXTURETYPE_2D:
+ samplerType = glu::TYPE_SAMPLER_2D_SHADOW;
+ break;
+ case TEXTURETYPE_CUBE_MAP:
+ samplerType = glu::TYPE_SAMPLER_CUBE_SHADOW;
+ break;
+ case TEXTURETYPE_CUBE_MAP_ARRAY:
+ samplerType = glu::TYPE_SAMPLER_CUBE_ARRAY_SHADOW;
+ break;
+ case TEXTURETYPE_2D_ARRAY:
+ samplerType = glu::TYPE_SAMPLER_2D_ARRAY_SHADOW;
+ break;
+ default:
+ DE_ASSERT(DE_FALSE);
+ }
+
+ switch (m_lookupSpec.function)
+ {
+ case FUNCTION_TEXTURE:
+ baseFuncName = "texture";
+ break;
+ case FUNCTION_TEXTURELOD:
+ baseFuncName = "textureLod";
+ break;
+ default:
+ DE_ASSERT(DE_FALSE);
+ }
+
+ bool isGL = glu::isContextTypeGLCore(m_renderCtx.getType());
+ glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_renderCtx.getType());
+
+ std::string shaderVersion = glu::getGLSLVersionDeclaration(glslVersion);
+
+ std::ostringstream vert;
+ std::ostringstream frag;
+ std::ostringstream& op = isVtxCase ? vert : frag;
+
+ std::string cubeMapArrayEXT = "";
+
+ // Check if we need to add the texture_cube_map_array extension.
+ if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
+ {
+ if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) &&
+ !glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 0)))
+ {
+ if (isGL)
+ {
+ cubeMapArrayEXT = "#extension GL_ARB_texture_cube_map_array : require\n";
+ }
+ else
+ {
+ glu::ContextInfo* info = glu::ContextInfo::create(m_renderCtx);
+ if (info->isExtensionSupported("GL_EXT_texture_cube_map_array"))
+ {
+ cubeMapArrayEXT = "#extension GL_EXT_texture_cube_map_array : require\n";
+ }
+ else
+ {
+ cubeMapArrayEXT = "#extension GL_OES_texture_cube_map_array : require\n";
+ }
+ }
+ }
+ }
+
+ vert << shaderVersion << "\n"
+ << "#extension GL_EXT_texture_shadow_lod : require\n\n";
+
+ vert << cubeMapArrayEXT;
+
+ vert << "in highp vec4 a_position;\n"
+ << "in " << coordPrecName << " " << coordTypeName << " a_in0;\n";
+
+ if (hasLodBias || hasSepShadowRef)
+ {
+ vert << "in " << coordPrecName << " vec4 a_in1;\n";
+ }
+
+ frag << shaderVersion << "\n"
+ << "#extension GL_EXT_texture_shadow_lod : require\n\n";
+
+ frag << cubeMapArrayEXT;
+
+ frag << "out mediump vec4 o_color;\n";
+
+ if (isVtxCase)
+ {
+ vert << "out mediump vec4 v_color;\n";
+ frag << "in mediump vec4 v_color;\n";
+ }
+ else
+ {
+ vert << "out " << coordPrecName << " " << coordTypeName << " v_texCoord;\n";
+ frag << "in " << coordPrecName << " " << coordTypeName << " v_texCoord;\n";
+
+ if (hasLodBias || hasSepShadowRef)
+ {
+ vert << "out " << coordPrecName << " vec4 v_lodShadowRef;\n";
+ frag << "in " << coordPrecName << " vec4 v_lodShadowRef;\n";
+ }
+ }
+
+ // Uniforms
+ op << "uniform highp " << glu::getDataTypeName(samplerType) << " u_sampler;\n"
+ << "uniform highp vec4 u_scale;\n"
+ << "uniform highp vec4 u_bias;\n";
+
+ vert << "\nvoid main()\n{\n"
+ << "\tgl_Position = a_position;\n";
+ frag << "\nvoid main()\n{\n";
+
+ if (isVtxCase)
+ vert << "\tv_color = ";
+ else
+ frag << "\to_color = ";
+
+ // Op.
+ {
+ const char* texCoord = isVtxCase ? "a_in0" : "v_texCoord";
+ const char* lodBias = isVtxCase ? "a_in1" : "v_lodShadowRef";
+
+ op << "vec4(" << baseFuncName;
+ if (m_lookupSpec.useOffset)
+ op << "Offset";
+ op << "(u_sampler, ";
+
+ op << texCoord;
+
+ if (m_lookupSpec.useSepRef)
+ {
+ op << ", " << lodBias << ".y";
+ }
+
+ if (functionHasLod(function))
+ {
+ op << ", " << lodBias << ".x";
+ }
+
+ if (m_lookupSpec.useOffset)
+ {
+ int offsetComps = 2;
+
+ op << ", ivec" << offsetComps << "(";
+ for (int ndx = 0; ndx < offsetComps; ndx++)
+ {
+ if (ndx != 0)
+ op << ", ";
+ op << m_lookupSpec.offset[ndx];
+ }
+ op << ")";
+ }
+
+ if (m_lookupSpec.useBias)
+ op << ", " << lodBias << ".x";
+
+ op << ")";
+
+ op << ", 0.0, 0.0, 1.0)";
+
+ op << ";\n";
+ }
+
+ if (isVtxCase)
+ frag << "\to_color = v_color;\n";
+ else
+ {
+ vert << "\tv_texCoord = a_in0;\n";
+
+ if (hasLodBias || hasSepShadowRef)
+ {
+ vert << "\tv_lodShadowRef = a_in1;\n";
+ }
+ }
+
+ vert << "}\n";
+ frag << "}\n";
+
+ m_vertShaderSource = vert.str();
+ m_fragShaderSource = frag.str();
+}
+
+void TextureShadowLodTestCase::deinit(void)
+{
+ ShaderRenderCase::deinit();
+
+ delete m_texture2D;
+ delete m_textureCube;
+ delete m_texture2DArray;
+
+ m_texture2D = DE_NULL;
+ m_textureCube = DE_NULL;
+ m_texture2DArray = DE_NULL;
+}
+
+void TextureShadowLodTestCase::setupUniforms(deUint32 programID, const tcu::Vec4&)
+{
+ const glw::Functions& gl = m_renderCtx.getFunctions();
+ gl.uniform1i(gl.getUniformLocation(programID, "u_sampler"), 0);
+ gl.uniform4fv(gl.getUniformLocation(programID, "u_scale"), 1, m_lookupParams.scale.getPtr());
+ gl.uniform4fv(gl.getUniformLocation(programID, "u_bias"), 1, m_lookupParams.bias.getPtr());
+}
+
+TextureShadowLodTest::TextureShadowLodTest(Context& context)
+ : TestCaseGroup(context, "ext_texture_shadow_lod", "Texture Access Function Tests")
+{
+}
+
+TextureShadowLodTest::~TextureShadowLodTest(void)
+{
+}
+
+enum CaseFlags
+{
+ VERTEX = (1 << 0),
+ FRAGMENT = (1 << 1),
+ BOTH = VERTEX | FRAGMENT
+};
+
+struct TexFuncCaseSpec
+{
+ const char* name;
+ TextureLookupSpec lookupSpec;
+ TextureSpec texSpec;
+ TexEvalFunc evalFunc;
+ deUint32 flags;
+};
+
+#define CASE_SPEC(NAME, FUNC, MINCOORD, MAXCOORD, USEBIAS, MINLOD, MAXLOD, USEOFFSET, OFFSET, USESEPREF, MINSEPREF, \
+ MAXSEPREF, TEXSPEC, EVALFUNC, FLAGS) \
+ { \
+ #NAME, TextureLookupSpec(FUNC, MINCOORD, MAXCOORD, USEBIAS, MINLOD, MAXLOD, USEOFFSET, OFFSET, USESEPREF, \
+ MINSEPREF, MAXSEPREF), \
+ TEXSPEC, EVALFUNC, FLAGS \
+ }
+
+static void createCaseGroup(TestCaseGroup* parent, const char* groupName, const char* groupDesc,
+ const TexFuncCaseSpec* cases, int numCases)
+{
+ tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), groupName, groupDesc);
+ parent->addChild(group);
+
+ for (int ndx = 0; ndx < numCases; ndx++)
+ {
+ std::string name = cases[ndx].name;
+ if (cases[ndx].flags & VERTEX)
+ group->addChild(new TextureShadowLodTestCase(parent->getContext(), (name + "_vertex").c_str(), "",
+ cases[ndx].lookupSpec, cases[ndx].texSpec, cases[ndx].evalFunc,
+ true));
+ if (cases[ndx].flags & FRAGMENT)
+ group->addChild(new TextureShadowLodTestCase(parent->getContext(), (name + "_fragment").c_str(), "",
+ cases[ndx].lookupSpec, cases[ndx].texSpec, cases[ndx].evalFunc,
+ false));
+ }
+}
+
+void TextureShadowLodTest::init(void)
+{
+ // Samplers
+ static const tcu::Sampler samplerShadowNoMipmap(
+ tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST,
+ tcu::Sampler::NEAREST, 0.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_LESS,
+ 0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
+ static const tcu::Sampler samplerShadowMipmap(
+ tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST_MIPMAP_NEAREST,
+ tcu::Sampler::NEAREST, 0.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_LESS,
+ 0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
+
+ // Default textures.
+ // Type Format W H D L Sampler
+
+ static const TextureSpec tex2DArrayShadow(TEXTURETYPE_2D_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 1,
+ samplerShadowNoMipmap);
+ static const TextureSpec texCubeShadow(TEXTURETYPE_CUBE_MAP, GL_DEPTH_COMPONENT16, 256, 256, 1, 1,
+ samplerShadowNoMipmap);
+ static const TextureSpec texCubeMipmapShadow(TEXTURETYPE_CUBE_MAP, GL_DEPTH_COMPONENT16, 256, 256, 1, 9,
+ samplerShadowMipmap);
+ static const TextureSpec texCubeArrayShadow(TEXTURETYPE_CUBE_MAP_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 1,
+ samplerShadowNoMipmap);
+ static const TextureSpec texCubeArrayMipmapShadow(TEXTURETYPE_CUBE_MAP_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 8,
+ samplerShadowMipmap);
+ static const TextureSpec tex2DArrayMipmapShadow(TEXTURETYPE_2D_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 8,
+ samplerShadowMipmap);
+
+ // texture() cases
+ static const TexFuncCaseSpec textureCases[] = {
+ // Name Function MinCoord MaxCoord Bias? MinLod MaxLod Offset? Offset Format EvalFunc Flags
+ CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
+ false, 0.0f, 0.0f, false, IVec3(0), false, 0.0f, 0.0f, tex2DArrayShadow, evalTexture2DArrayShadow,
+ VERTEX),
+ CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
+ false, 0.0f, 0.0f, false, IVec3(0), false, 0.0f, 0.0f, tex2DArrayMipmapShadow,
+ evalTexture2DArrayShadow, FRAGMENT),
+ CASE_SPEC(sampler2darrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
+ Vec4(1.5f, 2.3f, 3.5f, 1.0f), true, -2.0f, 2.0f, false, IVec3(0), false, 0.0f, 0.0f,
+ tex2DArrayMipmapShadow, evalTexture2DArrayShadowBias, FRAGMENT),
+
+ CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
+ Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, 0.0f, 0.0f, false, IVec3(0), true, 0.0f, 1.0f,
+ texCubeArrayShadow, evalTextureCubeArrayShadow, VERTEX),
+ CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
+ Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, 0.0f, 0.0f, false, IVec3(0), true, 0.0f, 1.0f,
+ texCubeArrayMipmapShadow, evalTextureCubeArrayShadow, FRAGMENT),
+ CASE_SPEC(samplercubearrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
+ Vec4(1.0f, 1.0f, 1.01f, 3.5f), true, -2.0, 2.0f, false, IVec3(0), true, 0.0f, 1.0f,
+ texCubeArrayMipmapShadow, evalTextureCubeArrayShadowBias, FRAGMENT),
+ };
+ createCaseGroup(this, "texture", "texture() Tests", textureCases, DE_LENGTH_OF_ARRAY(textureCases));
+
+ // textureOffset() cases
+ // \note _bias variants are not using mipmap thanks to wide allowed range for LOD computation
+ static const TexFuncCaseSpec textureOffsetCases[] = {
+ // Name Function MinCoord MaxCoord Bias? MinLod MaxLod Offset? Offset Format EvalFunc Flags
+ CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
+ false, 0.0f, 0.0f, true, IVec3(-8, 7, 0), false, 0.0f, 0.0f, tex2DArrayShadow,
+ evalTexture2DArrayShadowOffset, VERTEX),
+ CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
+ false, 0.0f, 0.0f, true, IVec3(7, -8, 0), false, 0.0f, 0.0f, tex2DArrayMipmapShadow,
+ evalTexture2DArrayShadowOffset, FRAGMENT),
+ CASE_SPEC(sampler2darrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
+ Vec4(1.5f, 2.3f, 3.5f, 1.0f), true, -2.0f, 2.0f, true, IVec3(7, -8, 0), false, 0.0f, 0.0f,
+ tex2DArrayMipmapShadow, evalTexture2DArrayShadowOffsetBias, FRAGMENT),
+ };
+ createCaseGroup(this, "textureoffset", "textureOffset() Tests", textureOffsetCases,
+ DE_LENGTH_OF_ARRAY(textureOffsetCases));
+
+ // textureLod() cases
+ static const TexFuncCaseSpec textureLodCases[] = {
+ // Name Function MinCoord MaxCoord Bias? MinLod MaxLod Offset? Offset Format EvalFunc Flags
+ CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
+ Vec4(1.5f, 2.3f, 3.5f, 1.0f), false, -1.0f, 8.0f, false, IVec3(0), false, 0.0f, 0.0f,
+ tex2DArrayMipmapShadow, evalTexture2DArrayShadowLod, BOTH),
+ CASE_SPEC(samplercubeshadow, FUNCTION_TEXTURELOD, Vec4(-1.0f, -1.0f, 1.01f, 0.0f),
+ Vec4(1.0f, 1.0f, 1.01f, 1.0f), false, -1.0f, 8.0f, false, IVec3(0), false, 0.0f, 0.0f,
+ texCubeMipmapShadow, evalTextureCubeShadowLod, BOTH),
+ CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
+ Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, -1.0f, 8.0f, false, IVec3(0), true, 0.0f, 1.0f,
+ texCubeArrayMipmapShadow, evalTextureCubeArrayShadowLod, FRAGMENT)
+ };
+ createCaseGroup(this, "texturelod", "textureLod() Tests", textureLodCases, DE_LENGTH_OF_ARRAY(textureLodCases));
+
+ // textureLodOffset() cases
+ static const TexFuncCaseSpec textureLodOffsetCases[] = {
+ // Name Function MinCoord MaxCoord Bias? MinLod MaxLod Offset? Offset Format EvalFunc Flags
+ CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
+ Vec4(1.5f, 2.3f, 3.5f, 1.0f), false, -1.0f, 9.0f, true, IVec3(-8, 7, 0), false, 0.0f, 0.0f,
+ tex2DArrayMipmapShadow, evalTexture2DArrayShadowLodOffset, BOTH),
+ };
+ createCaseGroup(this, "texturelodoffset", "textureLodOffset() Tests", textureLodOffsetCases,
+ DE_LENGTH_OF_ARRAY(textureLodOffsetCases));
+}
+} // namespace Functional
+} // namespace deqp