/*------------------------------------------------------------------------- * 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 #include #include 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 uni_image;\n" "\n" "void main()\n" "{\n" " point = ();\n" " memoryBarrier();\n" " color = ;\n" " imageStore(uni_image, point, color);\n" "}\n"; const char* stc_vertex_common = "#version 450\n" "\n" "in vec3 vertex;\n" "in inCoord;\n" "out 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 texCoord;\n" "out vec4 fragColor;\n" "\n" "layout (location = 1) uniform uni_in;\n" "layout (location = 2) uniform int widthCommitted;\n" "\n" "void main()\n" "{\n" " coord = texCoord;\n" " texSize = ();\n" " point = (coord * texSize);\n" " retValue,\n" " expValue,\n" " epsilon;\n" " retValue = (0);\n" " expValue = ;\n" " epsilon = ();\n" "\n" "\n" "\n" "\n" " ivec2 corner1 = ivec2(1, 1);\n" " ivec2 corner2 = ivec2(texSize.x - 1, texSize.y - 1);\n" "\n" " int code = (uni_in,\n" " ,\n" " retValue);\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 texCoord;\n" "out vec4 fragColor;\n" "\n" "layout (location = 1) uniform uni_in;\n" "\n" "void main()\n" "{\n" " coord = texCoord;\n" " texSize = ();\n" " point = (coord * texSize);\n" " retValue,\n" " expValue,\n" " epsilon;\n" " retValue = (0);\n" " expValue = ;\n" " epsilon = ();\n" "\n" "\n" "\n" "\n" "\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", ", "); 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", ", (0), "); 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", ", (0), (0), "); 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", ", (0), (0), (0), "); 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" || funcToken.name == "textureGradClampARB")) 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 vecExpData; std::vector 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); // 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 (int sample = 0; sample < mState.samples; ++sample) { std::string vertex = stc_vertex_common; std::string fragment = stc_fragment_lookupResidency; // Make token copy to work on FunctionToken f = funcToken; // Adjust shader source to texture format TokenStringsExt s = createLookupShaderTokens(target, verifyTarget, format, level, sample, f); replaceToken("", s.coordType.c_str(), vertex); replaceToken("", f.name.c_str(), fragment); replaceToken("", f.arguments.c_str(), fragment); replaceToken("", s.outputType.c_str(), fragment); replaceToken("", s.inputType.c_str(), fragment); replaceToken("", s.sizeDef.c_str(), fragment); replaceToken("", s.lod.c_str(), fragment); replaceToken("", s.lodDef.c_str(), fragment); replaceToken("", s.coordType.c_str(), fragment); replaceToken("", s.iCoordType.c_str(), fragment); replaceToken("", s.coordDef.c_str(), fragment); replaceToken("", s.pointType.c_str(), fragment); replaceToken("", s.pointDef.c_str(), fragment); replaceToken("", s.returnType.c_str(), fragment); replaceToken("", s.resultExpected.c_str(), fragment); replaceToken("", s.epsilon.c_str(), fragment); replaceToken("", s.sampleDef.c_str(), fragment); replaceToken("", s.refZDef.c_str(), fragment); replaceToken("", s.cubeMapArrayRefZDef.c_str(), fragment); replaceToken("", s.pointCoord.c_str(), fragment); replaceToken("", s.componentDef.c_str(), fragment); replaceToken("", s.cubeMapCoordDef.c_str(), fragment); replaceToken("", s.offsetArrayDef.c_str(), fragment); replaceToken("", s.formatDef.c_str(), fragment); replaceToken("", s.offsetType.c_str(), fragment); replaceToken("", s.nOffsetType.c_str(), fragment); replaceToken("", s.offsetDim.c_str(), fragment); replaceToken("", de::toString(width).c_str(), fragment); replaceToken("", de::toString(height).c_str(), fragment); replaceToken("", 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()) { for (GLint z = 0; z < depth; ++z) { 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"); // Use shader 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.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 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]) { m_testCtx.getLog() << tcu::TestLog::Message << mLog.str() << "Error detected at " << x << "," << y << "," << z << ": expected [" << (unsigned)dataRegion[0] << "] got [" << (unsigned)outDataRegion[0] << "]" << tcu::TestLog::EndMessage; result = false; goto out; } } } } 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; } } out: gl.bindFramebuffer(GL_FRAMEBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer"); gl.deleteFramebuffers(1, &fbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteFramebuffers"); 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 } }; // The fragment shader uses (z * 6) % 6 to calculate a cube face index. GLfloat cubeMapArrayZCoord = GLfloat(layer) / 6.0f + 0.01f; // The fragment shader does not modify w for layer selection. GLfloat cubeMapArrayWCoord = GLfloat(layer / 6); // Note: integer division const GLfloat texCoordCubeMapArray[16] = { 0.0f, 0.0f, cubeMapArrayZCoord, cubeMapArrayWCoord, 1.0f, 0.0f, cubeMapArrayZCoord, cubeMapArrayWCoord, 0.0f, 1.0f, cubeMapArrayZCoord, cubeMapArrayWCoord, 1.0f, 1.0f, cubeMapArrayZCoord, cubeMapArrayWCoord }; 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 if (target == GL_TEXTURE_CUBE_MAP_ARRAY) floatCoord = glu::va::Float("inCoord", 4, 4, 0, texCoordCubeMapArray); 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", ", "); 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", ", "); 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", ", (0), "); 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", ", (0), "); 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", ", (0), (0), "); 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", ", (0), (0), "); 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", ", (0), (0), (0), "); 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", ", (0), (0), (0), "); 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::const_iterator iter = mSupportedTargets.begin(); iter != mSupportedTargets.end(); ++iter) { const GLint& target = *iter; for (std::vector::const_iterator formIter = mSupportedInternalFormats.begin(); formIter != mSupportedInternalFormats.end(); ++formIter) { const GLint& format = *formIter; if (!caseAllowed(target, format)) continue; for (std::vector::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 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 GLint verifyTarget; if (target == GL_TEXTURE_2D_MULTISAMPLE || target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE) verifyTarget = GL_TEXTURE_2D; else verifyTarget = GL_TEXTURE_2D_ARRAY; TokenStrings s = createShaderTokens(target, verifyTarget, format, sample); GLint convFormat = format; if (format == GL_DEPTH_COMPONENT16) convFormat = GL_R16; // Change expected result as it has to be adjusted to different levels s.resultExpected = generateExpectedResult(s.returnType, level, convFormat); replaceToken("", s.inputType.c_str(), shader); replaceToken("", s.pointType.c_str(), shader); replaceToken("", s.pointDef.c_str(), shader); replaceToken("", s.returnType.c_str(), shader); replaceToken("", s.resultExpected.c_str(), shader); replaceToken("", s.sampleDef.c_str(), shader); ProgramSources sources; sources << ComputeSource(shader); // 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_TRUE /* 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 vecExpData; std::vector 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); // 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 (int sample = 0; sample < mState.samples; ++sample) { std::string vertex = stc_vertex_common; std::string fragment = stc_fragment_lookupColor; // Make token copy to work on FunctionToken f = funcToken; std::string functionDef = generateFunctionDef(f.name); // Adjust shader source to texture format TokenStringsExt s = createLookupShaderTokens(target, verifyTarget, format, level, sample, f); // Change expected result as it has to be adjusted to different levels s.resultExpected = generateExpectedResult(s.returnType, level, format); replaceToken("", s.coordType.c_str(), vertex); replaceToken("", functionDef.c_str(), fragment); replaceToken("", f.name.c_str(), fragment); replaceToken("", f.arguments.c_str(), fragment); replaceToken("", s.outputType.c_str(), fragment); replaceToken("", s.inputType.c_str(), fragment); replaceToken("", s.sizeDef.c_str(), fragment); replaceToken("", s.lod.c_str(), fragment); replaceToken("", s.lodDef.c_str(), fragment); replaceToken("", s.coordType.c_str(), fragment); replaceToken("", s.coordType.c_str(), fragment); replaceToken("", s.coordDef.c_str(), fragment); replaceToken("", s.pointType.c_str(), fragment); replaceToken("", s.pointDef.c_str(), fragment); replaceToken("", s.returnType.c_str(), fragment); replaceToken("", s.resultExpected.c_str(), fragment); replaceToken("", s.epsilon.c_str(), fragment); replaceToken("", s.sampleDef.c_str(), fragment); replaceToken("", s.refZDef.c_str(), fragment); replaceToken("", s.cubeMapArrayRefZDef.c_str(), fragment); replaceToken("", s.pointCoord.c_str(), fragment); replaceToken("", s.componentDef.c_str(), fragment); replaceToken("", s.cubeMapCoordDef.c_str(), fragment); replaceToken("", s.offsetArrayDef.c_str(), fragment); replaceToken("", s.formatDef.c_str(), fragment); replaceToken("", s.offsetType.c_str(), fragment); replaceToken("", s.nOffsetType.c_str(), fragment); replaceToken("", s.offsetDim.c_str(), fragment); replaceToken("", de::toString(width).c_str(), fragment); replaceToken("", de::toString(height).c_str(), fragment); replaceToken("", 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()) { for (GLint z = 0; z < depth; ++z) { 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"); // Use shader 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.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 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"); gl.deleteFramebuffers(1, &fbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteFramebuffers"); 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 and 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(" (uni_in,\n" " ,\n" " retValue);\n"); } else { return std::string(" retValue = (uni_in,\n" " );\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, GLint format) { if (format == GL_DEPTH_COMPONENT16) return std::string("(1, 0, 0, 0)"); else 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 */