+/*-------------------------------------------------------------------------
+ * OpenGL Conformance Test Suite
+ * -----------------------------
+ *
+ * Copyright (c) 2016 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 gl4cSparseTextureClampTests.cpp
+ * \brief Conformance tests for the GL_ARB_sparse_texture2 functionality.
+ */ /*-------------------------------------------------------------------*/
+
+#include "gl4cSparseTextureClampTests.hpp"
+#include "deStringUtil.hpp"
+#include "gl4cSparseTexture2Tests.hpp"
+#include "gl4cSparseTextureTests.hpp"
+#include "gluContextInfo.hpp"
+#include "gluDefs.hpp"
+#include "glwEnums.hpp"
+#include "glwFunctions.hpp"
+#include "tcuImageIO.hpp"
+#include "tcuTestLog.hpp"
+
+#include <cmath>
+#include <string.h>
+#include <vector>
+
+using namespace glw;
+using namespace glu;
+
+namespace gl4cts
+{
+
+const char* stc_compute_textureFill = "#version 430 core\n"
+ "\n"
+ "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
+ "\n"
+ "layout (location = 1) writeonly uniform highp <INPUT_TYPE> uni_image;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " <POINT_TYPE> point = <POINT_TYPE>(<POINT_DEF>);\n"
+ " memoryBarrier();\n"
+ " <RETURN_TYPE> color = <RETURN_TYPE><RESULT_EXPECTED>;\n"
+ " imageStore(uni_image, point<SAMPLE_DEF>, color);\n"
+ "}\n";
+
+const char* stc_vertex_common = "#version 450\n"
+ "\n"
+ "in vec3 vertex;\n"
+ "in <COORD_TYPE> inCoord;\n"
+ "out <COORD_TYPE> texCoord;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " texCoord = inCoord;\n"
+ " gl_Position = vec4(vertex, 1);\n"
+ "}\n";
+
+const char* stc_fragment_lookupResidency = "#version 450 core\n"
+ "\n"
+ "#extension GL_ARB_sparse_texture2 : enable\n"
+ "#extension GL_ARB_sparse_texture_clamp : enable\n"
+ "\n"
+ "in <COORD_TYPE> texCoord;\n"
+ "out vec4 fragColor;\n"
+ "\n"
+ "layout (location = 1<FORMAT_DEF>) uniform <INPUT_TYPE> uni_in;\n"
+ "layout (location = 2) uniform int widthCommitted;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " <COORD_TYPE> coord = texCoord;\n"
+ " <ICOORD_TYPE> texSize = <ICOORD_TYPE>(<SIZE_DEF>);\n"
+ " <POINT_TYPE> point = <POINT_TYPE>(coord * texSize);\n"
+ " <RETURN_TYPE> retValue,\n"
+ " expValue,\n"
+ " epsilon;\n"
+ " retValue = <RETURN_TYPE>(0);\n"
+ " expValue = <RETURN_TYPE><RESULT_EXPECTED>;\n"
+ " epsilon = <RETURN_TYPE>(<EPSILON>);\n"
+ "\n"
+ "<CUBE_MAP_COORD_DEF>\n"
+ "<OFFSET_ARRAY_DEF>\n"
+ "\n"
+ " ivec2 corner1 = ivec2(1, 1);\n"
+ " ivec2 corner2 = ivec2(texSize.x - 1, texSize.y - 1);\n"
+ "\n"
+ " int code = <FUNCTION>(uni_in,\n"
+ " <POINT_COORD><SAMPLE_DEF><ARGUMENTS>,\n"
+ " retValue<COMPONENT_DEF>);\n"
+ "\n"
+ " fragColor = vec4(1);\n"
+ "\n"
+ " if (point.x > corner1.x && point.y > corner1.y &&\n"
+ " point.x < corner2.x && point.y < corner2.y &&\n"
+ " point.x < widthCommitted - 1)\n"
+ " {\n"
+ " if (!sparseTexelsResidentARB(code) ||\n"
+ " any(greaterThan(retValue, expValue + epsilon)) ||\n"
+ " any(lessThan(retValue, expValue - epsilon)))\n"
+ " {\n"
+ " fragColor = vec4(0);\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " if (point.x > corner1.x && point.y > corner1.y &&\n"
+ " point.x < corner2.x && point.y < corner2.y &&\n"
+ " point.x >= widthCommitted + 1)\n"
+ " {\n"
+ " if (sparseTexelsResidentARB(code))\n"
+ " {\n"
+ " fragColor = vec4(0);\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+const char* stc_fragment_lookupColor = "#version 450 core\n"
+ "\n"
+ "#extension GL_ARB_sparse_texture2 : enable\n"
+ "#extension GL_ARB_sparse_texture_clamp : enable\n"
+ "\n"
+ "in <COORD_TYPE> texCoord;\n"
+ "out vec4 fragColor;\n"
+ "\n"
+ "layout (location = 1<FORMAT_DEF>) uniform <INPUT_TYPE> uni_in;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " <COORD_TYPE> coord = texCoord;\n"
+ " <ICOORD_TYPE> texSize = <ICOORD_TYPE>(<SIZE_DEF>);\n"
+ " <POINT_TYPE> point = <POINT_TYPE>(coord * texSize);\n"
+ " <RETURN_TYPE> retValue,\n"
+ " expValue,\n"
+ " epsilon;\n"
+ " retValue = <RETURN_TYPE>(0);\n"
+ " expValue = <RETURN_TYPE><RESULT_EXPECTED>;\n"
+ " epsilon = <RETURN_TYPE>(<EPSILON>);\n"
+ "\n"
+ "<CUBE_MAP_COORD_DEF>\n"
+ "<OFFSET_ARRAY_DEF>\n"
+ "\n"
+ "<FUNCTION_DEF>\n"
+ "\n"
+ " fragColor = vec4(1);\n"
+ "\n"
+ " if (any(greaterThan(retValue, expValue + epsilon)) ||\n"
+ " any(lessThan(retValue, expValue - epsilon)))\n"
+ " {\n"
+ " fragColor = vec4(0);\n"
+ " }\n"
+ "}\n";
+
+/** Constructor.
+ *
+ * @param context Rendering context
+ */
+SparseTextureClampLookupResidencyTestCase::SparseTextureClampLookupResidencyTestCase(deqp::Context& context)
+ : SparseTexture2LookupTestCase(
+ context, "SparseTextureClampLookupResidency",
+ "Verifies if sparse texture clamp lookup functions generates access residency information")
+{
+ /* Left blank intentionally */
+}
+
+/** Constructor.
+ *
+ * @param context Rendering context
+ */
+SparseTextureClampLookupResidencyTestCase::SparseTextureClampLookupResidencyTestCase(deqp::Context& context,
+ const char* name,
+ const char* description)
+ : SparseTexture2LookupTestCase(context, name, description)
+{
+ /* Left blank intentionally */
+}
+
+/** Stub init method */
+void SparseTextureClampLookupResidencyTestCase::init()
+{
+ SparseTextureCommitmentTestCase::init();
+ mSupportedInternalFormats.push_back(GL_DEPTH_COMPONENT16);
+
+ FunctionToken f;
+ f = FunctionToken("sparseTextureClampARB", ", <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("sparseTextureOffsetClampARB", ", <OFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("sparseTextureGradClampARB",
+ ", <NOFFSET_TYPE><OFFSET_DIM>(0), <NOFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken(
+ "sparseTextureGradOffsetClampARB",
+ ", <NOFFSET_TYPE><OFFSET_DIM>(0), <NOFFSET_TYPE><OFFSET_DIM>(0), <OFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+}
+
+/** Executes test iteration.
+ *
+ * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
+ */
+tcu::TestNode::IterateResult SparseTextureClampLookupResidencyTestCase::iterate()
+{
+ if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_sparse_texture_clamp"))
+ {
+ m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
+ return STOP;
+ }
+
+ return SparseTexture2LookupTestCase::iterate();
+}
+
+/** Check if specific lookup function is allowed for specific target and format
+ *
+ * @param target Target for which texture is binded
+ * @param format Texture internal format
+ * @param funcToken Texture lookup function structure
+ *
+ * @return Returns true if target/format combination is allowed, false otherwise.
+ */
+bool SparseTextureClampLookupResidencyTestCase::funcAllowed(GLint target, GLint format, FunctionToken& funcToken)
+{
+ if (funcToken.allowedTargets.find(target) == funcToken.allowedTargets.end())
+ return false;
+
+ if (format == GL_DEPTH_COMPONENT16)
+ {
+ if (target == GL_TEXTURE_CUBE_MAP_ARRAY && funcToken.name == "sparseTextureGradClampARB")
+ return false;
+ }
+
+ return true;
+}
+
+/** Verify if data stored in texture is as expected
+ *
+ * @param gl GL API functions
+ * @param target Target for which texture is binded
+ * @param format Texture internal format
+ * @param texture Texture object
+ * @param level Texture mipmap level
+ * @param funcToken Lookup function tokenize structure
+ *
+ * @return Returns true if data is as expected, false if not, throws an exception if error occurred.
+ */
+bool SparseTextureClampLookupResidencyTestCase::verifyLookupTextureData(const Functions& gl, GLint target, GLint format,
+ GLuint& texture, GLint level,
+ FunctionToken& funcToken)
+{
+ mLog << "Verify Lookup Residency Texture Data [function: " << funcToken.name << ", level: " << level << "] - ";
+
+ if (level > mState.levels - 1)
+ TCU_FAIL("Invalid level");
+
+ GLint width;
+ GLint height;
+ GLint depth;
+ SparseTextureUtils::getTextureLevelSize(target, mState, level, width, height, depth);
+
+ //Committed region is limited to 1/2 of width
+ GLint widthCommitted = width / 2;
+
+ if (widthCommitted == 0 || height == 0 || depth < mState.minDepth)
+ return true;
+
+ bool result = true;
+
+ if (target == GL_TEXTURE_CUBE_MAP)
+ depth = depth * 6;
+
+ GLint texSize = width * height;
+
+ std::vector<GLubyte> vecExpData;
+ std::vector<GLubyte> vecOutData;
+ vecExpData.resize(texSize);
+ vecOutData.resize(texSize);
+ GLubyte* exp_data = vecExpData.data();
+ GLubyte* out_data = vecOutData.data();
+
+ // Expected data is 255 because
+ deMemset(exp_data, 255, texSize);
+
+ // Make token copy to work on
+ FunctionToken f = funcToken;
+
+ // Create verifying texture
+ GLint verifyTarget = GL_TEXTURE_2D;
+ GLuint verifyTexture;
+ Texture::Generate(gl, verifyTexture);
+ Texture::Bind(gl, verifyTexture, verifyTarget);
+ Texture::Storage(gl, verifyTarget, 1, GL_R8, width, height, depth);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Texture::Storage");
+
+ GLuint fbo;
+ gl.genFramebuffers(1, &fbo);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers");
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+ gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, verifyTarget, verifyTexture, 0);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D");
+
+ gl.viewport(0, 0, width, height);
+
+ for (GLint z = 0; z < depth; ++z)
+ {
+ for (int sample = 0; sample < mState.samples; ++sample)
+ {
+ deMemset(out_data, 0, texSize);
+
+ Texture::Bind(gl, verifyTexture, verifyTarget);
+ Texture::SubImage(gl, verifyTarget, 0, 0, 0, 0, width, height, 0, GL_RED, GL_UNSIGNED_BYTE,
+ (GLvoid*)out_data);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Texture::SubImage");
+
+ std::string vertex = stc_vertex_common;
+ std::string fragment = stc_fragment_lookupResidency;
+
+ // Adjust shader source to texture format
+ TokenStringsExt s = createLookupShaderTokens(target, format, level, sample, f);
+
+ replaceToken("<COORD_TYPE>", s.coordType.c_str(), vertex);
+
+ replaceToken("<FUNCTION>", f.name.c_str(), fragment);
+ replaceToken("<ARGUMENTS>", f.arguments.c_str(), fragment);
+
+ replaceToken("<OUTPUT_TYPE>", s.outputType.c_str(), fragment);
+ replaceToken("<INPUT_TYPE>", s.inputType.c_str(), fragment);
+ replaceToken("<SIZE_DEF>", s.sizeDef.c_str(), fragment);
+ replaceToken("<LOD>", s.lod.c_str(), fragment);
+ replaceToken("<LOD_DEF>", s.lodDef.c_str(), fragment);
+ replaceToken("<COORD_TYPE>", s.coordType.c_str(), fragment);
+ replaceToken("<ICOORD_TYPE>", s.iCoordType.c_str(), fragment);
+ replaceToken("<COORD_DEF>", s.coordDef.c_str(), fragment);
+ replaceToken("<POINT_TYPE>", s.pointType.c_str(), fragment);
+ replaceToken("<POINT_DEF>", s.pointDef.c_str(), fragment);
+ replaceToken("<RETURN_TYPE>", s.returnType.c_str(), fragment);
+ replaceToken("<RESULT_EXPECTED>", s.resultExpected.c_str(), fragment);
+ replaceToken("<EPSILON>", s.epsilon.c_str(), fragment);
+ replaceToken("<SAMPLE_DEF>", s.sampleDef.c_str(), fragment);
+ replaceToken("<REFZ_DEF>", s.refZDef.c_str(), fragment);
+ replaceToken("<POINT_COORD>", s.pointCoord.c_str(), fragment);
+ replaceToken("<COMPONENT_DEF>", s.componentDef.c_str(), fragment);
+ replaceToken("<CUBE_MAP_COORD_DEF>", s.cubeMapCoordDef.c_str(), fragment);
+ replaceToken("<OFFSET_ARRAY_DEF>", s.offsetArrayDef.c_str(), fragment);
+ replaceToken("<FORMAT_DEF>", s.formatDef.c_str(), fragment);
+ replaceToken("<OFFSET_TYPE>", s.offsetType.c_str(), fragment);
+ replaceToken("<NOFFSET_TYPE>", s.nOffsetType.c_str(), fragment);
+ replaceToken("<OFFSET_DIM>", s.offsetDim.c_str(), fragment);
+
+ replaceToken("<TEX_WIDTH>", de::toString(width).c_str(), fragment);
+ replaceToken("<TEX_HEIGHT>", de::toString(height).c_str(), fragment);
+ replaceToken("<TEX_DEPTH>", de::toString(depth).c_str(), fragment);
+
+ ProgramSources sources = makeVtxFragSources(vertex.c_str(), fragment.c_str());
+
+ // Build and run shader
+ ShaderProgram program(m_context.getRenderContext(), sources);
+ if (program.isOk())
+ {
+ gl.useProgram(program.getProgram());
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram");
+
+ // Pass input sampler/image to shader
+ gl.activeTexture(GL_TEXTURE0);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glActiveTexture");
+ gl.uniform1i(1, 0 /* sampler_unit */);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i");
+
+ // Pass committed region width to shader
+ gl.uniform1i(2, widthCommitted /* committed region width */);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i");
+
+ gl.bindTexture(target, texture);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture");
+
+ gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ draw(target, z, program);
+
+ Texture::Bind(gl, verifyTexture, verifyTarget);
+ Texture::GetData(gl, 0, verifyTarget, GL_RED, GL_UNSIGNED_BYTE, (GLvoid*)out_data);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Texture::GetData");
+
+ //Verify only committed region
+ for (GLint y = 0; y < height; ++y)
+ for (GLint x = 0; x < width; ++x)
+ {
+ GLubyte* dataRegion = exp_data + x + y * width;
+ GLubyte* outDataRegion = out_data + x + y * width;
+ if (dataRegion[0] != outDataRegion[0])
+ result = false;
+ }
+ }
+ else
+ {
+ mLog << "Shader compilation failed (lookup residency) for target: " << target << ", format: " << format
+ << ", vertexInfoLog: " << program.getShaderInfo(SHADERTYPE_VERTEX).infoLog
+ << ", fragmentInfoLog: " << program.getShaderInfo(SHADERTYPE_FRAGMENT).infoLog
+ << ", programInfoLog: " << program.getProgramInfo().infoLog
+ << ", fragmentSource: " << fragment.c_str() << " - ";
+
+ result = false;
+ }
+ }
+ }
+
+ gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+
+ Texture::Delete(gl, verifyTexture);
+
+ return result;
+}
+
+void SparseTextureClampLookupResidencyTestCase::draw(GLint target, GLint layer, const ShaderProgram& program)
+{
+ const GLfloat texCoord1D[] = { 0.0f, 1.0f, 0.0f, 1.0f };
+
+ const GLfloat texCoord2D[] = {
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
+ };
+
+ const GLfloat texCoord3D[] = { 0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f };
+
+ const GLfloat texCoordCubeMap[6][12] = {
+ { 0.0f, 0.0f, 0.00f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f },
+ { 0.0f, 0.0f, 0.17f, 1.0f, 0.0f, 0.17f, 0.0f, 1.0f, 0.17f, 1.0f, 1.0f, 0.17f },
+ { 0.0f, 0.0f, 0.33f, 1.0f, 0.0f, 0.33f, 0.0f, 1.0f, 0.33f, 1.0f, 1.0f, 0.33f },
+ { 0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f },
+ { 0.0f, 0.0f, 0.67f, 1.0f, 0.0f, 0.67f, 0.0f, 1.0f, 0.67f, 1.0f, 1.0f, 0.67f },
+ { 0.0f, 0.0f, 0.83f, 1.0f, 0.0f, 0.83f, 0.0f, 1.0f, 0.83f, 1.0f, 1.0f, 0.83f }
+ };
+
+ const GLfloat vertices[] = {
+ -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
+ };
+
+ const GLuint indices[] = { 0, 1, 2, 1, 2, 3 };
+
+ VertexArrayBinding floatCoord;
+
+ if (target == GL_TEXTURE_1D || target == GL_TEXTURE_1D_ARRAY)
+ floatCoord = glu::va::Float("inCoord", 1, 4, 0, texCoord1D);
+ else if (target == GL_TEXTURE_3D)
+ floatCoord = glu::va::Float("inCoord", 3, 4, 0, texCoord3D);
+ else if (target == GL_TEXTURE_CUBE_MAP)
+ floatCoord = glu::va::Float("inCoord", 3, 4, 0, texCoordCubeMap[layer]);
+ else
+ floatCoord = glu::va::Float("inCoord", 2, 4, 0, texCoord2D);
+
+ glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("vertex", 3, 4, 0, vertices), floatCoord };
+
+ glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
+ glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(indices), indices));
+}
+
+/** Constructor.
+ *
+ * @param context Rendering context
+ */
+SparseTextureClampLookupColorTestCase::SparseTextureClampLookupColorTestCase(deqp::Context& context)
+ : SparseTextureClampLookupResidencyTestCase(
+ context, "SparseTextureClampLookupColor",
+ "Verifies if sparse and non-sparse texture clamp lookup functions works as expected")
+{
+ /* Left blank intentionally */
+}
+
+/** Stub init method */
+void SparseTextureClampLookupColorTestCase::init()
+{
+ SparseTextureCommitmentTestCase::init();
+ mSupportedTargets.push_back(GL_TEXTURE_1D);
+ mSupportedTargets.push_back(GL_TEXTURE_1D_ARRAY);
+ mSupportedInternalFormats.push_back(GL_DEPTH_COMPONENT16);
+
+ FunctionToken f;
+ f = FunctionToken("sparseTextureClampARB", ", <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("textureClampARB", ", <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_1D);
+ f.allowedTargets.insert(GL_TEXTURE_1D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("sparseTextureOffsetClampARB", ", <OFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("textureOffsetClampARB", ", <OFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_1D);
+ f.allowedTargets.insert(GL_TEXTURE_1D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("sparseTextureGradClampARB",
+ ", <NOFFSET_TYPE><OFFSET_DIM>(0), <NOFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken("textureGradClampARB", ", <NOFFSET_TYPE><OFFSET_DIM>(0), <NOFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_1D);
+ f.allowedTargets.insert(GL_TEXTURE_1D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP);
+ f.allowedTargets.insert(GL_TEXTURE_CUBE_MAP_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken(
+ "sparseTextureGradOffsetClampARB",
+ ", <NOFFSET_TYPE><OFFSET_DIM>(0), <NOFFSET_TYPE><OFFSET_DIM>(0), <OFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+
+ f = FunctionToken(
+ "textureGradOffsetClampARB",
+ ", <NOFFSET_TYPE><OFFSET_DIM>(0), <NOFFSET_TYPE><OFFSET_DIM>(0), <OFFSET_TYPE><OFFSET_DIM>(0), <LOD>");
+ f.allowedTargets.insert(GL_TEXTURE_1D);
+ f.allowedTargets.insert(GL_TEXTURE_1D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_2D);
+ f.allowedTargets.insert(GL_TEXTURE_2D_ARRAY);
+ f.allowedTargets.insert(GL_TEXTURE_3D);
+ mFunctions.push_back(f);
+}
+
+/** Executes test iteration.
+ *
+ * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
+ */
+tcu::TestNode::IterateResult SparseTextureClampLookupColorTestCase::iterate()
+{
+ if (!m_context.getContextInfo().isExtensionSupported("GL_ARB_sparse_texture_clamp"))
+ {
+ m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
+ return STOP;
+ }
+
+ const Functions& gl = m_context.getRenderContext().getFunctions();
+
+ bool result = true;
+
+ GLuint texture;
+
+ for (std::vector<glw::GLint>::const_iterator iter = mSupportedTargets.begin(); iter != mSupportedTargets.end();
+ ++iter)
+ {
+ const GLint& target = *iter;
+
+ for (std::vector<glw::GLint>::const_iterator formIter = mSupportedInternalFormats.begin();
+ formIter != mSupportedInternalFormats.end(); ++formIter)
+ {
+ const GLint& format = *formIter;
+
+ if (!caseAllowed(target, format))
+ continue;
+
+ for (std::vector<FunctionToken>::const_iterator tokIter = mFunctions.begin(); tokIter != mFunctions.end();
+ ++tokIter)
+ {
+ // Check if target is allowed for current lookup function
+ FunctionToken funcToken = *tokIter;
+ if (!funcAllowed(target, format, funcToken))
+ continue;
+
+ bool isSparse = false;
+ if (funcToken.name.find("sparse", 0) != std::string::npos)
+ isSparse = true;
+
+ mLog.str("");
+ mLog << "Testing sparse texture lookup color functions for target: " << target << ", format: " << format
+ << " - ";
+
+ if (isSparse)
+ sparseAllocateTexture(gl, target, format, texture, 3);
+ else
+ allocateTexture(gl, target, format, texture, 3);
+
+ if (format == GL_DEPTH_COMPONENT16)
+ setupDepthMode(gl, target, texture);
+
+ int l;
+ int maxLevels = 0;
+ for (l = 0; l < mState.levels; ++l)
+ {
+ if (!isSparse || commitTexturePage(gl, target, format, texture, l))
+ {
+ writeDataToTexture(gl, target, format, texture, l);
+ maxLevels = l;
+ }
+ }
+
+ for (l = 0; l <= maxLevels; ++l)
+ {
+ result = result && verifyLookupTextureData(gl, target, format, texture, l, funcToken);
+
+ if (!result)
+ break;
+ }
+
+ Texture::Delete(gl, texture);
+
+ if (!result)
+ {
+ m_testCtx.getLog() << tcu::TestLog::Message << mLog.str() << "Fail" << tcu::TestLog::EndMessage;
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
+ return STOP;
+ }
+ }
+ }
+ }
+
+ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+ return STOP;
+}
+
+/** Writing data to generated texture using compute shader
+ *
+ * @param gl GL API functions
+ * @param target Target for which texture is binded
+ * @param format Texture internal format
+ * @param texture Texture object
+ *
+ * @return Returns true if no error occurred, otherwise throws an exception.
+ */
+bool SparseTextureClampLookupColorTestCase::writeDataToTexture(const Functions& gl, GLint target, GLint format,
+ GLuint& texture, GLint level)
+{
+ mLog << "Fill Texture with shader [level: " << level << "] - ";
+
+ if (level > mState.levels - 1)
+ TCU_FAIL("Invalid level");
+
+ GLint width;
+ GLint height;
+ GLint depth;
+ SparseTextureUtils::getTextureLevelSize(target, mState, level, width, height, depth);
+
+ if (width > 0 && height > 0 && depth >= mState.minDepth)
+ {
+ if (target == GL_TEXTURE_CUBE_MAP)
+ depth = depth * 6;
+
+ GLint texSize = width * height * depth * mState.format.getPixelSize();
+
+ std::vector<GLubyte> vecData;
+ vecData.resize(texSize);
+ GLubyte* data = vecData.data();
+
+ deMemset(data, 255, texSize);
+
+ for (GLint sample = 0; sample < mState.samples; ++sample)
+ {
+ std::string shader = stc_compute_textureFill;
+
+ // Adjust shader source to texture format
+ TokenStrings s = createShaderTokens(target, format, sample);
+
+ // Change expected result as it has to be adjusted to different levels
+ s.resultExpected = generateExpectedResult(s.returnType, level);
+
+ replaceToken("<INPUT_TYPE>", s.inputType.c_str(), shader);
+ replaceToken("<POINT_TYPE>", s.pointType.c_str(), shader);
+ replaceToken("<POINT_DEF>", s.pointDef.c_str(), shader);
+ replaceToken("<RETURN_TYPE>", s.returnType.c_str(), shader);
+ replaceToken("<RESULT_EXPECTED>", s.resultExpected.c_str(), shader);
+ replaceToken("<SAMPLE_DEF>", s.sampleDef.c_str(), shader);
+
+ ProgramSources sources;
+ sources << ComputeSource(shader);
+
+ GLint convFormat = format;
+ if (format == GL_DEPTH_COMPONENT16)
+ convFormat = GL_R16;
+
+ // Build and run shader
+ ShaderProgram program(m_context.getRenderContext(), sources);
+ if (program.isOk())
+ {
+ gl.useProgram(program.getProgram());
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram");
+ gl.bindImageTexture(0 /* unit */, texture, level /* level */, GL_FALSE /* layered */, 0 /* layer */,
+ GL_WRITE_ONLY, convFormat);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture");
+ gl.uniform1i(1, 0 /* image_unit */);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i");
+ gl.dispatchCompute(width, height, depth);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute");
+ gl.memoryBarrier(GL_ALL_BARRIER_BITS);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glMemoryBarrier");
+ }
+ else
+ {
+ mLog << "Compute shader compilation failed (writing) for target: " << target << ", format: " << format
+ << ", sample: " << sample << ", infoLog: " << program.getShaderInfo(SHADERTYPE_COMPUTE).infoLog
+ << ", shaderSource: " << shader.c_str() << " - ";
+ }
+ }
+ }
+
+ return true;
+}
+
+/** Verify if data stored in texture is as expected
+ *
+ * @param gl GL API functions
+ * @param target Target for which texture is binded
+ * @param format Texture internal format
+ * @param texture Texture object
+ * @param level Texture mipmap level
+ * @param funcToken Lookup function tokenize structure
+ *
+ * @return Returns true if data is as expected, false if not, throws an exception if error occurred.
+ */
+bool SparseTextureClampLookupColorTestCase::verifyLookupTextureData(const Functions& gl, GLint target, GLint format,
+ GLuint& texture, GLint level,
+ FunctionToken& funcToken)
+{
+ mLog << "Verify Lookup Color Texture Data [function: " << funcToken.name << ", level: " << level << "] - ";
+
+ if (level > mState.levels - 1)
+ TCU_FAIL("Invalid level");
+
+ GLint width;
+ GLint height;
+ GLint depth;
+ SparseTextureUtils::getTextureLevelSize(target, mState, level, width, height, depth);
+
+ if (width == 0 || height == 0 || depth < mState.minDepth)
+ return true;
+
+ bool result = true;
+
+ if (target == GL_TEXTURE_CUBE_MAP)
+ depth = depth * 6;
+
+ GLint texSize = width * height;
+
+ std::vector<GLubyte> vecExpData;
+ std::vector<GLubyte> vecOutData;
+ vecExpData.resize(texSize);
+ vecOutData.resize(texSize);
+ GLubyte* exp_data = vecExpData.data();
+ GLubyte* out_data = vecOutData.data();
+
+ // Expected data is 255 because
+ deMemset(exp_data, 255, texSize);
+
+ // Make token copy to work on
+ FunctionToken f = funcToken;
+
+ // Create verifying texture
+ GLint verifyTarget = GL_TEXTURE_2D;
+ GLuint verifyTexture;
+ Texture::Generate(gl, verifyTexture);
+ Texture::Bind(gl, verifyTexture, verifyTarget);
+ Texture::Storage(gl, verifyTarget, 1, GL_R8, width, height, depth);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Texture::Storage");
+
+ GLuint fbo;
+ gl.genFramebuffers(1, &fbo);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers");
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+ gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, verifyTarget, verifyTexture, 0);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D");
+
+ gl.viewport(0, 0, width, height);
+
+ for (GLint z = 0; z < depth; ++z)
+ {
+ for (int sample = 0; sample < mState.samples; ++sample)
+ {
+ deMemset(out_data, 0, texSize);
+
+ Texture::Bind(gl, verifyTexture, verifyTarget);
+ Texture::SubImage(gl, verifyTarget, 0, 0, 0, 0, width, height, 0, GL_RED, GL_UNSIGNED_BYTE,
+ (GLvoid*)out_data);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Texture::SubImage");
+
+ std::string vertex = stc_vertex_common;
+ std::string fragment = stc_fragment_lookupColor;
+
+ std::string functionDef = generateFunctionDef(f.name);
+
+ // Adjust shader source to texture format
+ TokenStringsExt s = createLookupShaderTokens(target, format, level, sample, f);
+
+ // Change expected result as it has to be adjusted to different levels
+ s.resultExpected = generateExpectedResult(s.returnType, level);
+
+ replaceToken("<COORD_TYPE>", s.coordType.c_str(), vertex);
+
+ replaceToken("<FUNCTION_DEF>", functionDef.c_str(), fragment);
+ replaceToken("<FUNCTION>", f.name.c_str(), fragment);
+ replaceToken("<ARGUMENTS>", f.arguments.c_str(), fragment);
+
+ replaceToken("<OUTPUT_TYPE>", s.outputType.c_str(), fragment);
+ replaceToken("<INPUT_TYPE>", s.inputType.c_str(), fragment);
+ replaceToken("<SIZE_DEF>", s.sizeDef.c_str(), fragment);
+ replaceToken("<LOD>", s.lod.c_str(), fragment);
+ replaceToken("<LOD_DEF>", s.lodDef.c_str(), fragment);
+ replaceToken("<COORD_TYPE>", s.coordType.c_str(), fragment);
+ replaceToken("<ICOORD_TYPE>", s.coordType.c_str(), fragment);
+ replaceToken("<COORD_DEF>", s.coordDef.c_str(), fragment);
+ replaceToken("<POINT_TYPE>", s.pointType.c_str(), fragment);
+ replaceToken("<POINT_DEF>", s.pointDef.c_str(), fragment);
+ replaceToken("<RETURN_TYPE>", s.returnType.c_str(), fragment);
+ replaceToken("<RESULT_EXPECTED>", s.resultExpected.c_str(), fragment);
+ replaceToken("<EPSILON>", s.epsilon.c_str(), fragment);
+ replaceToken("<SAMPLE_DEF>", s.sampleDef.c_str(), fragment);
+ replaceToken("<REFZ_DEF>", s.refZDef.c_str(), fragment);
+ replaceToken("<POINT_COORD>", s.pointCoord.c_str(), fragment);
+ replaceToken("<COMPONENT_DEF>", s.componentDef.c_str(), fragment);
+ replaceToken("<CUBE_MAP_COORD_DEF>", s.cubeMapCoordDef.c_str(), fragment);
+ replaceToken("<OFFSET_ARRAY_DEF>", s.offsetArrayDef.c_str(), fragment);
+ replaceToken("<FORMAT_DEF>", s.formatDef.c_str(), fragment);
+ replaceToken("<OFFSET_TYPE>", s.offsetType.c_str(), fragment);
+ replaceToken("<NOFFSET_TYPE>", s.nOffsetType.c_str(), fragment);
+ replaceToken("<OFFSET_DIM>", s.offsetDim.c_str(), fragment);
+
+ replaceToken("<TEX_WIDTH>", de::toString(width).c_str(), fragment);
+ replaceToken("<TEX_HEIGHT>", de::toString(height).c_str(), fragment);
+ replaceToken("<TEX_DEPTH>", de::toString(depth).c_str(), fragment);
+
+ ProgramSources sources = makeVtxFragSources(vertex.c_str(), fragment.c_str());
+
+ // Build and run shader
+ ShaderProgram program(m_context.getRenderContext(), sources);
+ if (program.isOk())
+ {
+ gl.useProgram(program.getProgram());
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram");
+
+ // Pass input sampler/image to shader
+ gl.activeTexture(GL_TEXTURE0);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glActiveTexture");
+ gl.uniform1i(1, 0 /* sampler_unit */);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i");
+
+ gl.bindTexture(target, texture);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture");
+
+ gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ draw(target, z, program);
+
+ Texture::Bind(gl, verifyTexture, verifyTarget);
+ Texture::GetData(gl, 0, verifyTarget, GL_RED, GL_UNSIGNED_BYTE, (GLvoid*)out_data);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Texture::GetData");
+
+ //Verify only committed region
+ for (GLint y = 0; y < height; ++y)
+ for (GLint x = 0; x < width; ++x)
+ {
+ GLubyte* dataRegion = exp_data + x + y * width;
+ GLubyte* outDataRegion = out_data + x + y * width;
+ if (dataRegion[0] != outDataRegion[0])
+ result = false;
+ }
+ }
+ else
+ {
+ mLog << "Shader compilation failed (lookup color) for target: " << target << ", format: " << format
+ << ", vertexInfoLog: " << program.getShaderInfo(SHADERTYPE_VERTEX).infoLog
+ << ", fragmentInfoLog: " << program.getShaderInfo(SHADERTYPE_FRAGMENT).infoLog
+ << ", programInfoLog: " << program.getProgramInfo().infoLog
+ << ", fragmentSource: " << fragment.c_str() << " - ";
+
+ result = false;
+ }
+ }
+ }
+
+ gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer");
+
+ Texture::Delete(gl, verifyTexture);
+
+ return result;
+}
+
+/** Preparing texture. Function overridden to increase textures size.
+ *
+ * @param gl GL API functions
+ * @param target Target for which texture is binded
+ * @param format Texture internal format
+ * @param texture Texture object
+ *
+ * @return Returns true if no error occurred, otherwise throws an exception.
+ */
+bool SparseTextureClampLookupColorTestCase::prepareTexture(const Functions& gl, GLint target, GLint format,
+ GLuint& texture)
+{
+ Texture::Generate(gl, texture);
+ Texture::Bind(gl, texture, target);
+
+ mState.minDepth = SparseTextureUtils::getTargetDepth(target);
+ SparseTextureUtils::getTexturePageSizes(gl, target, format, mState.pageSizeX, mState.pageSizeY, mState.pageSizeZ);
+
+ //The <width> and <height> has to be equal for cube map textures
+ if (target == GL_TEXTURE_CUBE_MAP || target == GL_TEXTURE_CUBE_MAP_ARRAY)
+ {
+ if (mState.pageSizeX > mState.pageSizeY)
+ mState.pageSizeY = mState.pageSizeX;
+ else if (mState.pageSizeX < mState.pageSizeY)
+ mState.pageSizeX = mState.pageSizeY;
+ }
+
+ mState.width = 4 * mState.pageSizeX;
+ mState.height = 4 * mState.pageSizeY;
+ mState.depth = 4 * mState.pageSizeZ * mState.minDepth;
+
+ mState.format = glu::mapGLInternalFormat(format);
+
+ return true;
+}
+
+/** Commit texture page using texPageCommitment function. Function overridden to commit whole texture region.
+ *
+ * @param gl GL API functions
+ * @param target Target for which texture is binded
+ * @param format Texture internal format
+ * @param texture Texture object
+ * @param level Texture mipmap level
+ *
+ * @return Returns true if commitment is done properly, false if commitment is not allowed or throws exception if error occurred.
+ */
+bool SparseTextureClampLookupColorTestCase::commitTexturePage(const Functions& gl, GLint target, GLint format,
+ GLuint& texture, GLint level)
+{
+ mLog << "Commit Region [level: " << level << "] - ";
+
+ if (level > mState.levels - 1)
+ TCU_FAIL("Invalid level");
+
+ // Avoid not allowed commitments
+ if (!isInPageSizesRange(target, level) || !isPageSizesMultiplication(target, level))
+ {
+ mLog << "Skip commitment [level: " << level << "] - ";
+ return false;
+ }
+
+ GLint width;
+ GLint height;
+ GLint depth;
+ SparseTextureUtils::getTextureLevelSize(target, mState, level, width, height, depth);
+
+ if (target == GL_TEXTURE_CUBE_MAP)
+ depth = 6 * depth;
+
+ Texture::Bind(gl, texture, target);
+ texPageCommitment(gl, target, format, texture, level, 0, 0, 0, width, height, depth, GL_TRUE);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "texPageCommitment");
+
+ return true;
+}
+
+/** Check if current texture size for level is greater or equal page size in a corresponding direction
+ *
+ * @param target Target for which texture is binded
+ * @param level Texture mipmap level
+ *
+ * @return Returns true if the texture size condition is fulfilled, false otherwise.
+ */
+bool SparseTextureClampLookupColorTestCase::isInPageSizesRange(GLint target, GLint level)
+{
+ GLint width;
+ GLint height;
+ GLint depth;
+ SparseTextureUtils::getTextureLevelSize(target, mState, level, width, height, depth);
+
+ if (target == GL_TEXTURE_CUBE_MAP)
+ depth = 6 * depth;
+
+ if (width >= mState.pageSizeX && height >= mState.pageSizeY && (mState.minDepth == 0 || depth >= mState.pageSizeZ))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/** Check if current texture size for level is page size multiplication in a corresponding direction
+ *
+ * @param target Target for which texture is binded
+ * @param level Texture mipmap level
+ *
+ * @return Returns true if the texture size condition is fulfilled, false otherwise.
+ */
+bool SparseTextureClampLookupColorTestCase::isPageSizesMultiplication(GLint target, GLint level)
+{
+ GLint width;
+ GLint height;
+ GLint depth;
+ SparseTextureUtils::getTextureLevelSize(target, mState, level, width, height, depth);
+
+ if (target == GL_TEXTURE_CUBE_MAP)
+ depth = 6 * depth;
+
+ if ((width % mState.pageSizeX) == 0 && (height % mState.pageSizeY) == 0 && (depth % mState.pageSizeZ) == 0)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/** Constructor.
+ *
+ * @param funcName Tested function name.
+ *
+ * @return Returns shader source code part that uses lookup function to fetch texel from texture.
+ */
+std::string SparseTextureClampLookupColorTestCase::generateFunctionDef(std::string funcName)
+{
+ if (funcName.find("sparse", 0) != std::string::npos)
+ {
+ return std::string(" <FUNCTION>(uni_in,\n"
+ " <POINT_COORD><SAMPLE_DEF><ARGUMENTS>,\n"
+ " retValue<COMPONENT_DEF>);\n");
+ }
+ else
+ {
+ return std::string(" retValue<COMPONENT_DEF> = <FUNCTION>(uni_in,\n"
+ " <POINT_COORD><SAMPLE_DEF><ARGUMENTS>);\n");
+ }
+}
+
+/** Constructor.
+ *
+ * @param returnType Expected result variable type.
+ *
+ * @return Returns shader source token that represent expected lookup result value.
+ */
+std::string SparseTextureClampLookupColorTestCase::generateExpectedResult(std::string returnType, GLint level)
+{
+ if (returnType == "vec4")
+ return std::string("(") + de::toString(0.5f + (float)level / 10) + std::string(", 0, 0, 1)");
+ else
+ return std::string("(") + de::toString(level * 10) + std::string(", 0, 0, 1)");
+}
+
+/** Constructor.
+ *
+ * @param context Rendering context.
+ */
+SparseTextureClampTests::SparseTextureClampTests(deqp::Context& context)
+ : TestCaseGroup(context, "sparse_texture_clamp_tests",
+ "Verify conformance of CTS_ARB_sparse_texture_clamp implementation")
+{
+}
+
+/** Initializes the test group contents. */
+void SparseTextureClampTests::init()
+{
+ addChild(new ShaderExtensionTestCase(m_context, "GL_ARB_sparse_texture_clamp"));
+ addChild(new SparseTextureClampLookupResidencyTestCase(m_context));
+ addChild(new SparseTextureClampLookupColorTestCase(m_context));
+}
+
+} /* gl4cts namespace */