/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Shader API tests. *//*--------------------------------------------------------------------*/ #include "es3fShaderApiTests.hpp" #include "es3fApiCase.hpp" #include "tcuTestLog.hpp" #include "gluRenderContext.hpp" #include "gluShaderProgram.hpp" #include "gluShaderUtil.hpp" #include "gluDrawUtil.hpp" #include "gluContextInfo.hpp" #include "gluCallLogWrapper.hpp" #include "glwFunctions.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "deString.h" #include "deRandom.hpp" #include "deStringUtil.hpp" #include <string> #include <sstream> #include <vector> #include <map> using namespace glw; // GL types namespace deqp { namespace gles3 { namespace Functional { using tcu::TestLog; namespace { enum ShaderSourceCaseFlags { CASE_EXPLICIT_SOURCE_LENGTHS = 1, CASE_RANDOM_NULL_TERMINATED = 2 }; struct ShaderSources { std::vector<std::string> strings; std::vector<int> lengths; }; // Simple shaders const char* getSimpleShaderSource (const glu::ShaderType shaderType) { const char* simpleVertexShaderSource = "#version 300 es\n" "void main (void)\n" "{\n" " gl_Position = vec4(0.0);\n" "}\n"; const char* simpleFragmentShaderSource = "#version 300 es\n" "layout(location = 0) out mediump vec4 o_fragColor;\n" "void main (void)\n" "{\n" " o_fragColor = vec4(0.0);\n" "}\n"; switch (shaderType) { case glu::SHADERTYPE_VERTEX: return simpleVertexShaderSource; case glu::SHADERTYPE_FRAGMENT: return simpleFragmentShaderSource; default: DE_ASSERT(DE_FALSE); } return 0; } void setShaderSources (glu::Shader& shader, const ShaderSources& sources) { std::vector<const char*> cStrings (sources.strings.size(), 0); for (size_t ndx = 0; ndx < sources.strings.size(); ndx++) cStrings[ndx] = sources.strings[ndx].c_str(); if (sources.lengths.size() > 0) shader.setSources((int)cStrings.size(), &cStrings[0], &sources.lengths[0]); else shader.setSources((int)cStrings.size(), &cStrings[0], 0); } void sliceSourceString (const std::string& in, ShaderSources& out, const int numSlices, const size_t paddingLength = 0) { DE_ASSERT(numSlices > 0); const size_t sliceSize = in.length() / numSlices; const size_t sliceSizeRemainder = in.length() - (sliceSize * numSlices); const std::string padding (paddingLength, 'E'); for (int ndx = 0; ndx < numSlices; ndx++) { out.strings.push_back(in.substr(ndx * sliceSize, sliceSize) + padding); if (paddingLength > 0) out.lengths.push_back((int)sliceSize); } if (sliceSizeRemainder > 0) { const std::string lastString = in.substr(numSlices * sliceSize); const int lastStringLength = (int)lastString.length(); out.strings.push_back(lastString + padding); if (paddingLength > 0) out.lengths.push_back(lastStringLength); } } void queryShaderInfo (glu::RenderContext& renderCtx, deUint32 shader, glu::ShaderInfo& info) { const glw::Functions& gl = renderCtx.getFunctions(); info.compileOk = false; info.compileTimeUs = 0; info.infoLog.clear(); // Query source, status & log. { int compileStatus = 0; int sourceLen = 0; int infoLogLen = 0; int unusedLen; gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); gl.getShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLen); gl.getShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv()"); info.compileOk = compileStatus != GL_FALSE; if (sourceLen > 0) { std::vector<char> source(sourceLen); gl.getShaderSource(shader, (int)source.size(), &unusedLen, &source[0]); info.source = std::string(&source[0], sourceLen); } if (infoLogLen > 0) { std::vector<char> infoLog(infoLogLen); gl.getShaderInfoLog(shader, (int)infoLog.size(), &unusedLen, &infoLog[0]); info.infoLog = std::string(&infoLog[0], infoLogLen); } } } // Draw test quad void drawWithProgram (glu::RenderContext& renderCtx, deUint32 program) { const glw::Functions& gl = renderCtx.getFunctions(); const float position[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, +1.0f, 0.0f, 1.0f, +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, 0.0f, 1.0f }; const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 }; gl.useProgram(program); { glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 4, 4, 0, &position[0]) }; glu::draw(renderCtx, program, DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0])); } GLU_EXPECT_NO_ERROR(gl.getError(), "Draw test quad"); } // Shader source generator class SourceGenerator { public: virtual ~SourceGenerator (void) {} virtual std::string next (const glu::ShaderType shaderType) = 0; virtual bool finished (const glu::ShaderType shaderType) const = 0; }; class ConstantShaderGenerator : public SourceGenerator { public: ConstantShaderGenerator (de::Random& rnd) : m_rnd(rnd) {} ~ConstantShaderGenerator (void) {} bool finished (const glu::ShaderType shaderType) const { DE_UNREF(shaderType); return false; } std::string next (const glu::ShaderType shaderType); private: de::Random m_rnd; }; std::string ConstantShaderGenerator::next (const glu::ShaderType shaderType) { DE_ASSERT(shaderType == glu::SHADERTYPE_VERTEX || shaderType == glu::SHADERTYPE_FRAGMENT); const float value = m_rnd.getFloat(0.0f, 1.0f); const std::string valueString = de::toString(value); const std::string outputName = (shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "o_fragColor"; std::ostringstream out; out << "#version 300 es\n"; if (shaderType == glu::SHADERTYPE_FRAGMENT) out << "layout(location = 0) out mediump vec4 o_fragColor;\n"; out << "void main (void)\n"; out << "{\n"; out << " " << outputName << " = vec4(" << valueString << ");\n"; out << "}\n"; return out.str(); } // Shader allocation utility class ShaderAllocator { public: ShaderAllocator (glu::RenderContext& context, SourceGenerator& generator); ~ShaderAllocator (void); bool hasShader (const glu::ShaderType shaderType); void setSource (const glu::ShaderType shaderType); glu::Shader& createShader (const glu::ShaderType shaderType); void deleteShader (const glu::ShaderType shaderType); glu::Shader& get (const glu::ShaderType shaderType) { DE_ASSERT(hasShader(shaderType)); return *m_shaders[shaderType]; } private: const glu::RenderContext& m_context; SourceGenerator& m_srcGen; std::map<glu::ShaderType, glu::Shader*> m_shaders; }; ShaderAllocator::ShaderAllocator (glu::RenderContext& context, SourceGenerator& generator) : m_context (context) , m_srcGen (generator) { } ShaderAllocator::~ShaderAllocator (void) { for (std::map<glu::ShaderType, glu::Shader*>::iterator shaderIter = m_shaders.begin(); shaderIter != m_shaders.end(); shaderIter++) delete shaderIter->second; m_shaders.clear(); } bool ShaderAllocator::hasShader (const glu::ShaderType shaderType) { if (m_shaders.find(shaderType) != m_shaders.end()) return true; else return false; } glu::Shader& ShaderAllocator::createShader (const glu::ShaderType shaderType) { DE_ASSERT(!this->hasShader(shaderType)); glu::Shader* const shader = new glu::Shader(m_context, shaderType); m_shaders[shaderType] = shader; this->setSource(shaderType); return *shader; } void ShaderAllocator::deleteShader (const glu::ShaderType shaderType) { DE_ASSERT(this->hasShader(shaderType)); delete m_shaders[shaderType]; m_shaders.erase(shaderType); } void ShaderAllocator::setSource (const glu::ShaderType shaderType) { DE_ASSERT(this->hasShader(shaderType)); DE_ASSERT(!m_srcGen.finished(shaderType)); const std::string source = m_srcGen.next(shaderType); const char* const cSource = source.c_str(); m_shaders[shaderType]->setSources(1, &cSource, 0); } // Logging utilities void logShader (TestLog& log, glu::RenderContext& renderCtx, glu::Shader& shader) { glu::ShaderInfo info; queryShaderInfo(renderCtx, shader.getShader(), info); log << TestLog::Shader(getLogShaderType(shader.getType()), info.source, info.compileOk, info.infoLog); } void logProgram (TestLog& log, glu::RenderContext& renderCtx, glu::Program& program, ShaderAllocator& shaders) { log << TestLog::ShaderProgram(program.getLinkStatus(), program.getInfoLog()); for (int shaderTypeInt = 0; shaderTypeInt < glu::SHADERTYPE_LAST; shaderTypeInt++) { const glu::ShaderType shaderType = (glu::ShaderType)shaderTypeInt; if (shaders.hasShader(shaderType)) logShader(log, renderCtx, shaders.get(shaderType)); } log << TestLog::EndShaderProgram; } void logVertexFragmentProgram (TestLog& log, glu::RenderContext& renderCtx, glu::Program& program, glu::Shader& vertShader, glu::Shader& fragShader) { DE_ASSERT(vertShader.getType() == glu::SHADERTYPE_VERTEX && fragShader.getType() == glu::SHADERTYPE_FRAGMENT); log << TestLog::ShaderProgram(program.getLinkStatus(), program.getInfoLog()); logShader(log, renderCtx, vertShader); logShader(log, renderCtx, fragShader); log << TestLog::EndShaderProgram; } } // anonymous // Simple glCreateShader() case class CreateShaderCase : public ApiCase { public: CreateShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ApiCase (context, name, desc) , m_shaderType (shaderType) { } void test (void) { const GLuint shaderObject = glCreateShader(glu::getGLShaderType(m_shaderType)); TCU_CHECK(shaderObject != 0); glDeleteShader(shaderObject); } private: const glu::ShaderType m_shaderType; }; // Simple glCompileShader() case class CompileShaderCase : public ApiCase { public: CompileShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ApiCase (context, name, desc) , m_shaderType (shaderType) { } bool checkCompileStatus (const GLuint shaderObject) { GLint compileStatus = -1; glGetShaderiv(shaderObject, GL_COMPILE_STATUS, &compileStatus); GLU_CHECK(); return (compileStatus == GL_TRUE); } void test (void) { const char* shaderSource = getSimpleShaderSource(m_shaderType); const GLuint shaderObject = glCreateShader(glu::getGLShaderType(m_shaderType)); TCU_CHECK(shaderObject != 0); glShaderSource(shaderObject, 1, &shaderSource, 0); glCompileShader(shaderObject); TCU_CHECK(checkCompileStatus(shaderObject)); glDeleteShader(shaderObject); } private: const glu::ShaderType m_shaderType; }; // Base class for simple program API tests class SimpleProgramCase : public ApiCase { public: SimpleProgramCase (Context& context, const char* name, const char* desc) : ApiCase (context, name, desc) , m_vertShader (0) , m_fragShader (0) , m_program (0) { } virtual ~SimpleProgramCase (void) { } virtual void compileShaders (void) { const char* vertSource = getSimpleShaderSource(glu::SHADERTYPE_VERTEX); const char* fragSource = getSimpleShaderSource(glu::SHADERTYPE_FRAGMENT); const GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); const GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); TCU_CHECK(vertShader != 0); TCU_CHECK(fragShader != 0); glShaderSource(vertShader, 1, &vertSource, 0); glCompileShader(vertShader); glShaderSource(fragShader, 1, &fragSource, 0); glCompileShader(fragShader); GLU_CHECK(); m_vertShader = vertShader; m_fragShader = fragShader; } void linkProgram (void) { const GLuint program = glCreateProgram(); TCU_CHECK(program != 0); glAttachShader(program, m_vertShader); glAttachShader(program, m_fragShader); GLU_CHECK(); glLinkProgram(program); m_program = program; } void cleanup (void) { glDeleteShader(m_vertShader); glDeleteShader(m_fragShader); glDeleteProgram(m_program); } protected: GLuint m_vertShader; GLuint m_fragShader; GLuint m_program; }; // glDeleteShader() case class DeleteShaderCase : public SimpleProgramCase { public: DeleteShaderCase (Context& context, const char* name, const char* desc) : SimpleProgramCase (context, name, desc) { } bool checkDeleteStatus(GLuint shader) { GLint deleteStatus = -1; glGetShaderiv(shader, GL_DELETE_STATUS, &deleteStatus); GLU_CHECK(); return (deleteStatus == GL_TRUE); } void deleteShaders (void) { glDeleteShader(m_vertShader); glDeleteShader(m_fragShader); GLU_CHECK(); } void test (void) { compileShaders(); linkProgram(); GLU_CHECK(); deleteShaders(); TCU_CHECK(checkDeleteStatus(m_vertShader) && checkDeleteStatus(m_fragShader)); glDeleteProgram(m_program); TCU_CHECK(!(glIsShader(m_vertShader) || glIsShader(m_fragShader))); } }; // Simple glLinkProgram() case class LinkVertexFragmentCase : public SimpleProgramCase { public: LinkVertexFragmentCase (Context& context, const char* name, const char* desc) : SimpleProgramCase (context, name, desc) { } bool checkLinkStatus (const GLuint programObject) { GLint linkStatus = -1; glGetProgramiv(programObject, GL_LINK_STATUS, &linkStatus); GLU_CHECK(); return (linkStatus == GL_TRUE); } void test (void) { compileShaders(); linkProgram(); GLU_CHECK_MSG("Linking failed."); TCU_CHECK_MSG(checkLinkStatus(m_program), "Fail, expected LINK_STATUS to be TRUE."); cleanup(); } }; class ShaderSourceReplaceCase : public ApiCase { public: ShaderSourceReplaceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ApiCase (context, name, desc) , m_shaderType (shaderType) { } std::string generateFirstSource (void) { return getSimpleShaderSource(m_shaderType); } std::string generateSecondSource (void) { std::ostringstream out; out << "#version 300 es\n"; out << "precision mediump float;\n"; if (m_shaderType == glu::SHADERTYPE_FRAGMENT) out << "layout(location = 0) out mediump vec4 o_fragColor;\n"; out << "void main()\n"; out << "{\n"; out << " float variable = 1.0f;\n"; if (m_shaderType == glu::SHADERTYPE_VERTEX) out << " gl_Position = vec4(variable);\n"; else if (m_shaderType == glu::SHADERTYPE_FRAGMENT) out << " o_fragColor = vec4(variable);\n"; out << "}\n"; return out.str(); } GLint getSourceLength (glu::Shader& shader) { GLint sourceLength = 0; glGetShaderiv(shader.getShader(), GL_SHADER_SOURCE_LENGTH, &sourceLength); GLU_CHECK(); return sourceLength; } std::string readSource (glu::Shader& shader) { const GLint sourceLength = getSourceLength(shader); std::vector<char> sourceBuffer (sourceLength + 1); glGetShaderSource(shader.getShader(), (GLsizei)sourceBuffer.size(), 0, &sourceBuffer[0]); return std::string(&sourceBuffer[0]); } void verifyShaderSourceReplaced (glu::Shader& shader, const std::string& firstSource, const std::string& secondSource) { TestLog& log = m_testCtx.getLog(); const std::string result = readSource(shader); if (result == firstSource) { log << TestLog::Message << "Fail, source was not replaced." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Shader source nor replaced"); } else if (result != secondSource) { log << TestLog::Message << "Fail, invalid shader source." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid source"); } } void test (void) { TestLog& log = m_testCtx.getLog(); glu::Shader shader (m_context.getRenderContext(), m_shaderType); const std::string firstSourceStr = generateFirstSource(); const std::string secondSourceStr = generateSecondSource(); const char* firstSource = firstSourceStr.c_str(); const char* secondSource = secondSourceStr.c_str(); log << TestLog::Message << "Setting shader source." << TestLog::EndMessage; shader.setSources(1, &firstSource, 0); GLU_CHECK(); log << TestLog::Message << "Replacing shader source." << TestLog::EndMessage; shader.setSources(1, &secondSource, 0); GLU_CHECK(); verifyShaderSourceReplaced(shader, firstSourceStr, secondSourceStr); } private: glu::ShaderType m_shaderType; }; // glShaderSource() split source case class ShaderSourceSplitCase : public ApiCase { public: ShaderSourceSplitCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType, const int numSlices, const deUint32 flags = 0) : ApiCase (context, name, desc) , m_rnd (deStringHash(getName()) ^ 0x4fb2337d) , m_shaderType (shaderType) , m_numSlices (numSlices) , m_explicitLengths ((flags & CASE_EXPLICIT_SOURCE_LENGTHS) != 0) , m_randomNullTerm ((flags & CASE_RANDOM_NULL_TERMINATED) != 0) { DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT); } virtual ~ShaderSourceSplitCase (void) { } std::string generateFullSource (void) { std::ostringstream out; out << "#version 300 es\n"; out << "precision mediump float;\n"; if (m_shaderType == glu::SHADERTYPE_FRAGMENT) out << "layout(location = 0) out mediump vec4 o_fragColor;\n"; out << "void main()\n"; out << "{\n"; out << " float variable = 1.0f;\n"; if (m_shaderType == glu::SHADERTYPE_VERTEX) out << " gl_Position = vec4(variable);\n"; else if (m_shaderType == glu::SHADERTYPE_FRAGMENT) out << " o_fragColor = vec4(variable);\n"; out << "}\n"; return out.str(); } void insertRandomNullTermStrings (ShaderSources& sources) { const int numInserts = de::max(m_numSlices >> 2, 1); std::vector<int> indices (sources.strings.size(), 0); DE_ASSERT(sources.lengths.size() > 0); DE_ASSERT(sources.lengths.size() == sources.strings.size()); for (int i = 0; i < (int)sources.strings.size(); i++) indices[i] = i; m_rnd.shuffle(indices.begin(), indices.end()); for (int i = 0; i < numInserts; i++) { const int ndx = indices[i]; const int unpaddedLength = sources.lengths[ndx]; const std::string unpaddedString = sources.strings[ndx].substr(0, unpaddedLength); sources.strings[ndx] = unpaddedString; sources.lengths[ndx] = m_rnd.getInt(-10, -1); } } void generateSources (ShaderSources& sources) { const size_t paddingLength = (m_explicitLengths ? 10 : 0); std::string str = generateFullSource(); sliceSourceString(str, sources, m_numSlices, paddingLength); if (m_randomNullTerm) insertRandomNullTermStrings(sources); } void buildProgram (glu::Shader& shader) { TestLog& log = m_testCtx.getLog(); glu::RenderContext& renderCtx = m_context.getRenderContext(); const glu::ShaderType supportShaderType = (m_shaderType == glu::SHADERTYPE_FRAGMENT ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT); const char* supportShaderSource = getSimpleShaderSource(supportShaderType); glu::Shader supportShader (renderCtx, supportShaderType); glu::Program program (renderCtx); supportShader.setSources(1, &supportShaderSource, 0); supportShader.compile(); program.attachShader(shader.getShader()); program.attachShader(supportShader.getShader()); program.link(); if (m_shaderType == glu::SHADERTYPE_VERTEX) logVertexFragmentProgram(log, renderCtx, program, shader, supportShader); else logVertexFragmentProgram(log, renderCtx, program, supportShader, shader); } void test (void) { TestLog& log = m_testCtx.getLog(); glu::RenderContext& renderCtx = m_context.getRenderContext(); ShaderSources sources; glu::Shader shader (renderCtx, m_shaderType); generateSources(sources); setShaderSources(shader, sources); shader.compile(); buildProgram(shader); if (!shader.getCompileStatus()) { log << TestLog::Message << "Compilation failed." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed"); } } private: de::Random m_rnd; const glu::ShaderType m_shaderType; const int m_numSlices; const bool m_explicitLengths; const bool m_randomNullTerm; }; // Base class for program state persistence cases class ProgramStateCase : public ApiCase { public: ProgramStateCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType); virtual ~ProgramStateCase (void) {} void buildProgram (glu::Program& program, ShaderAllocator& shaders); void verify (glu::Program& program, const glu::ProgramInfo& reference); void test (void); virtual void executeForProgram (glu::Program& program, ShaderAllocator& shaders) = 0; protected: de::Random m_rnd; const glu::ShaderType m_shaderType; }; ProgramStateCase::ProgramStateCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ApiCase (context, name, desc) , m_rnd (deStringHash(name) ^ 0x713de0ca) , m_shaderType (shaderType) { DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT); } void ProgramStateCase::buildProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& vertShader = shaders.createShader(glu::SHADERTYPE_VERTEX); glu::Shader& fragShader = shaders.createShader(glu::SHADERTYPE_FRAGMENT); vertShader.compile(); fragShader.compile(); program.attachShader(vertShader.getShader()); program.attachShader(fragShader.getShader()); program.link(); logProgram(log, m_context.getRenderContext(), program, shaders); } void ProgramStateCase::verify (glu::Program& program, const glu::ProgramInfo& reference) { TestLog& log = m_testCtx.getLog(); const glu::ProgramInfo& programInfo = program.getInfo(); if (!programInfo.linkOk) { log << TestLog::Message << "Fail, link status may only change as a result of linking or loading a program binary." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Link status changed"); } if (programInfo.linkTimeUs != reference.linkTimeUs) { log << TestLog::Message << "Fail, reported link time changed." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Link time changed"); } if (programInfo.infoLog != reference.infoLog) { log << TestLog::Message << "Fail, program infolog changed." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Infolog changed"); } } void ProgramStateCase::test (void) { TestLog& log = m_testCtx.getLog(); glu::RenderContext& renderCtx = m_context.getRenderContext(); ConstantShaderGenerator sourceGen (m_rnd); ShaderAllocator shaders (renderCtx, sourceGen); glu::Program program (renderCtx); buildProgram(program, shaders); if (program.getLinkStatus()) { glu::ProgramInfo programInfo = program.getInfo(); executeForProgram(program, shaders); verify(program, programInfo); logProgram(log, renderCtx, program, shaders); } else { log << TestLog::Message << "Fail, couldn't link program." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Linking failed"); } } // Program state case utilities namespace { template<class T> void addProgramStateCase (TestCaseGroup* group, Context& context, const std::string& name, const std::string& desc) { for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++) { const glu::ShaderType shaderType = (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX; const std::string shaderTypeName = getShaderTypeName(shaderType); const std::string caseName = name + "_" + shaderTypeName; const std::string caseDesc = "Build program, " + desc + ", for " + shaderTypeName + " shader."; group->addChild(new T(context, caseName.c_str(), caseDesc.c_str(), shaderType)); } } } // anonymous // Specialized program state cases class ProgramStateDetachShaderCase : public ProgramStateCase { public: ProgramStateDetachShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramStateCase (context, name, desc, shaderType) { } virtual ~ProgramStateDetachShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Detaching " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); } }; class ProgramStateReattachShaderCase : public ProgramStateCase { public: ProgramStateReattachShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramStateCase (context, name, desc, shaderType) { } virtual ~ProgramStateReattachShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Reattaching " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); program.attachShader(caseShader.getShader()); } }; class ProgramStateDeleteShaderCase : public ProgramStateCase { public: ProgramStateDeleteShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramStateCase (context, name, desc, shaderType) { } virtual ~ProgramStateDeleteShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Deleting " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); shaders.deleteShader(m_shaderType); } }; class ProgramStateReplaceShaderCase : public ProgramStateCase { public: ProgramStateReplaceShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramStateCase (context, name, desc, shaderType) { } virtual ~ProgramStateReplaceShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Deleting and replacing " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); shaders.deleteShader(m_shaderType); program.attachShader(shaders.createShader(m_shaderType).getShader()); } }; class ProgramStateRecompileShaderCase : public ProgramStateCase { public: ProgramStateRecompileShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramStateCase (context, name, desc, shaderType) { } virtual ~ProgramStateRecompileShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Recompiling " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; caseShader.compile(); DE_UNREF(program); } }; class ProgramStateReplaceSourceCase : public ProgramStateCase { public: ProgramStateReplaceSourceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramStateCase (context, name, desc, shaderType) { } virtual ~ProgramStateReplaceSourceCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Replacing " + std::string(getShaderTypeName(m_shaderType)) + " shader source and recompiling" << TestLog::EndMessage; shaders.setSource(m_shaderType); caseShader.compile(); DE_UNREF(program); } }; // Program binary utilities namespace { struct ProgramBinary { GLenum format; std::vector<deUint8> data; }; bool programBinariesEqual (const ProgramBinary& first, const ProgramBinary& second) { if ((first.format != second.format) || (first.data.size() != second.data.size())) return false; return std::equal(first.data.begin(), first.data.end(), second.data.begin()); } } // anonymous // Base class for program binary cases class ProgramBinaryCase : public TestCase, protected glu::CallLogWrapper { public: ProgramBinaryCase (Context& context, const char* name, const char* desc); virtual ~ProgramBinaryCase (void); void getBinaryFormats (std::vector<GLenum>& out); bool isFormatSupported (const glw::GLenum format) const; void getProgramBinary (ProgramBinary& out, GLuint program); void loadProgramBinary (ProgramBinary& binary, GLuint program); void verifyProgramBinary (ProgramBinary& binary); void init (void); IterateResult iterate (void); virtual void test (void) = 0; protected: std::vector<GLenum> m_formats; }; ProgramBinaryCase::ProgramBinaryCase (Context& context, const char* name, const char* desc) : TestCase (context, name, desc) , CallLogWrapper (context.getRenderContext().getFunctions(), context.getTestContext().getLog()) { } ProgramBinaryCase::~ProgramBinaryCase (void) { } void ProgramBinaryCase::getBinaryFormats (std::vector<GLenum>& out) { GLint numFormats = -1; glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats); out.clear(); if (numFormats > 0) { out.resize(numFormats, 0); glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, (GLint*)&out[0]); } } bool ProgramBinaryCase::isFormatSupported (const glw::GLenum format) const { return (std::find(m_formats.begin(), m_formats.end(), format) != m_formats.end()); } void ProgramBinaryCase::getProgramBinary (ProgramBinary& out, GLuint program) { GLint binaryLength = -1; glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength); if (binaryLength > 0) { GLsizei actualLength; GLenum format; out.data.clear(); out.data.resize(binaryLength, 0); GLU_CHECK_CALL(glGetProgramBinary(program, (GLsizei)out.data.size(), &actualLength, &format, &(out.data[0]))); TCU_CHECK(actualLength == binaryLength); out.format = format; } } void ProgramBinaryCase::loadProgramBinary (ProgramBinary& binary, GLuint program) { glProgramBinary(program, binary.format, &binary.data[0], (GLsizei)binary.data.size()); GLU_CHECK_MSG("Failed to load program binary."); } void ProgramBinaryCase::verifyProgramBinary (ProgramBinary& binary) { TestLog& log = m_testCtx.getLog(); if (!isFormatSupported(binary.format)) { log << TestLog::Message << "Program binary format " << binary.format << " is not among the supported formats reported by the platform." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid format"); } } void ProgramBinaryCase::init (void) { getBinaryFormats(m_formats); } tcu::TestNode::IterateResult ProgramBinaryCase::iterate (void) { TestLog& log = m_testCtx.getLog(); if (m_formats.empty()) { log << TestLog::Message << "No program binary formats are supported." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); enableLogging(true); test(); } return STOP; } // Simple program binary case class ProgramBinarySimpleCase : public ProgramBinaryCase { public: ProgramBinarySimpleCase (Context& context, const char* name, const char* desc) : ProgramBinaryCase(context, name, desc) { } virtual ~ProgramBinarySimpleCase (void) { } void test (void) { const std::string vertSrc = getSimpleShaderSource(glu::SHADERTYPE_VERTEX); const std::string fragSrc = getSimpleShaderSource(glu::SHADERTYPE_FRAGMENT); const glu::ProgramSources sources = glu::makeVtxFragSources(vertSrc, fragSrc); glu::ShaderProgram program (m_context.getRenderContext(), sources); if (program.isOk()) { ProgramBinary binary; getProgramBinary(binary, program.getProgram()); verifyProgramBinary(binary); } } }; // Program binary uniform reset case class ProgramBinaryUniformResetCase : public ProgramBinaryCase { public: ProgramBinaryUniformResetCase (Context& context, const char* name, const char* desc) : ProgramBinaryCase (context, name, desc) , m_rnd (deStringHash(name) ^ 0xf2b48c6a) { } virtual ~ProgramBinaryUniformResetCase (void) { } std::string getShaderSource (const glu::ShaderType shaderType) const { const char* vertSrc = "#version 300 es\n" "uniform bool u_boolVar;\n" "uniform highp int u_intVar;\n" "uniform highp float u_floatVar;\n\n" "in highp vec4 a_position;\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; const char* fragSrc = "#version 300 es\n" "uniform bool u_boolVar;\n" "uniform highp int u_intVar;\n" "uniform highp float u_floatVar;\n\n" "layout(location = 0) out mediump vec4 o_fragColor;\n\n" "void main (void)\n" "{\n" " mediump float refAll = float(u_boolVar) + float(u_intVar) + u_floatVar;\n" " o_fragColor = vec4(refAll);\n" "}\n"; DE_ASSERT(shaderType == glu::SHADERTYPE_VERTEX || shaderType == glu::SHADERTYPE_FRAGMENT); return (shaderType == glu::SHADERTYPE_VERTEX) ? vertSrc : fragSrc; } void setUniformsRandom (glu::ShaderProgram& program) { TestLog& log = m_testCtx.getLog(); const deUint32 glProg = program.getProgram(); log << TestLog::Message << "Setting uniforms to random non-zero values." << TestLog::EndMessage; glUseProgram(glProg); { const GLint boolLoc = glGetUniformLocation(glProg, "u_boolVar"); const GLint intLoc = glGetUniformLocation(glProg, "u_intVar"); const GLint floatLoc = glGetUniformLocation(glProg, "u_floatVar"); const deInt32 intVal = m_rnd.getInt(1, 1000); const float floatVal = m_rnd.getFloat(1.0, 1000.0); glUniform1i(boolLoc, GL_TRUE); glUniform1f(floatLoc, floatVal); glUniform1i(intLoc, intVal); } } void verifyUniformInt (glu::ShaderProgram& program, const std::string& name) { const GLint intLoc = glGetUniformLocation(program.getProgram(), name.c_str()); GLint intVar = -1; glGetUniformiv(program.getProgram(), intLoc, &intVar); if (intVar != 0) { m_testCtx.getLog() << TestLog::Message << "Fail, expected zero value for " << name << ", received: " << intVar << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Uniform value not reset"); } } void verifyUniformFloat (glu::ShaderProgram& program, const std::string& name) { const GLint floatLoc = glGetUniformLocation(program.getProgram(), name.c_str()); GLfloat floatVar = -1; glGetUniformfv(program.getProgram(), floatLoc, &floatVar); if (floatVar != 0.0f) { m_testCtx.getLog() << TestLog::Message << "Fail, expected zero value for " << name << ", received: " << de::toString(floatVar) << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Uniform value not reset"); } } void verifyUniformsReset (glu::ShaderProgram& program) { m_testCtx.getLog() << TestLog::Message << "Verifying uniform reset to 0/false." << TestLog::EndMessage; verifyUniformInt (program, "u_boolVar"); verifyUniformInt (program, "u_intVar"); verifyUniformFloat (program, "u_floatVar"); } void test (void) { TestLog& log = m_testCtx.getLog(); const std::string vertSrc = getShaderSource(glu::SHADERTYPE_VERTEX); const std::string fragSrc = getShaderSource(glu::SHADERTYPE_FRAGMENT); const glu::ProgramSources sources = glu::makeVtxFragSources(vertSrc, fragSrc); glu::ShaderProgram program (m_context.getRenderContext(), sources); log << program; TCU_CHECK_MSG(program.isOk(), "Couldn't build program"); { ProgramBinary binary; getProgramBinary(binary, program.getProgram()); verifyProgramBinary(binary); setUniformsRandom(program); log << TestLog::Message << "Rendering test image and reloading binary" << TestLog::EndMessage; drawWithProgram(m_context.getRenderContext(), program.getProgram()); loadProgramBinary(binary, program.getProgram()); verifyUniformsReset(program); } } private: de::Random m_rnd; }; // Base class for program state persistence cases class ProgramBinaryPersistenceCase : public ProgramBinaryCase { public: ProgramBinaryPersistenceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType); virtual ~ProgramBinaryPersistenceCase (void) {} void buildProgram (glu::Program& program, ShaderAllocator& shaders); void test (void); virtual void executeForProgram (glu::Program& program, ShaderAllocator& shaders) = 0; virtual void verify (glu::Program& program, const ProgramBinary& binary); protected: de::Random m_rnd; const glu::ShaderType m_shaderType; }; ProgramBinaryPersistenceCase::ProgramBinaryPersistenceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryCase (context, name, desc) , m_rnd (deStringHash(name) ^ 0x713de0ca) , m_shaderType (shaderType) { DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT); } void ProgramBinaryPersistenceCase::buildProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& vertShader = shaders.createShader(glu::SHADERTYPE_VERTEX); glu::Shader& fragShader = shaders.createShader(glu::SHADERTYPE_FRAGMENT); vertShader.compile(); fragShader.compile(); program.attachShader(vertShader.getShader()); program.attachShader(fragShader.getShader()); program.link(); logProgram(log, m_context.getRenderContext(), program, shaders); } void ProgramBinaryPersistenceCase::verify (glu::Program& program, const ProgramBinary& binary) { TestLog& log = m_testCtx.getLog(); ProgramBinary currentBinary; getProgramBinary(currentBinary, program.getProgram()); if (!programBinariesEqual(binary, currentBinary)) { log << TestLog::Message << "Fail, program binary may only change as a result of linking or loading." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program binary changed"); } } void ProgramBinaryPersistenceCase::test (void) { TestLog& log = m_testCtx.getLog(); glu::RenderContext& renderCtx = m_context.getRenderContext(); ConstantShaderGenerator sourceGen (m_rnd); ShaderAllocator shaders (renderCtx, sourceGen); glu::Program program (renderCtx); buildProgram(program, shaders); if (program.getLinkStatus()) { ProgramBinary binary; getProgramBinary(binary, program.getProgram()); executeForProgram(program, shaders); verify(program, binary); logProgram(log, renderCtx, program, shaders); } else { log << TestLog::Message << "Fail, couldn't link program." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Linking failed"); } } // Program state case utilities namespace { template<class T> void addProgramBinaryPersistenceCase (TestCaseGroup* group, Context& context, const std::string& name, const std::string& desc) { for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++) { const glu::ShaderType shaderType = (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX; const std::string shaderTypeName = getShaderTypeName(shaderType); const std::string caseName = name + "_" + shaderTypeName; const std::string caseDesc = "Build program, " + desc + ", for " + shaderTypeName + " shader."; group->addChild(new T(context, caseName.c_str(), caseDesc.c_str(), shaderType)); } } } // anonymous // Specialized program state cases class ProgramBinaryPersistenceDetachShaderCase : public ProgramBinaryPersistenceCase { public: ProgramBinaryPersistenceDetachShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryPersistenceCase (context, name, desc, shaderType) { } virtual ~ProgramBinaryPersistenceDetachShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Detaching " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); } }; class ProgramBinaryPersistenceReattachShaderCase : public ProgramBinaryPersistenceCase { public: ProgramBinaryPersistenceReattachShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryPersistenceCase (context, name, desc, shaderType) { } virtual ~ProgramBinaryPersistenceReattachShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Reattaching " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); program.attachShader(caseShader.getShader()); } }; class ProgramBinaryPersistenceDeleteShaderCase : public ProgramBinaryPersistenceCase { public: ProgramBinaryPersistenceDeleteShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryPersistenceCase (context, name, desc, shaderType) { } virtual ~ProgramBinaryPersistenceDeleteShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Deleting " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); shaders.deleteShader(m_shaderType); } }; class ProgramBinaryPersistenceReplaceShaderCase : public ProgramBinaryPersistenceCase { public: ProgramBinaryPersistenceReplaceShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryPersistenceCase (context, name, desc, shaderType) { } virtual ~ProgramBinaryPersistenceReplaceShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Deleting and replacing " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; program.detachShader(caseShader.getShader()); shaders.deleteShader(m_shaderType); program.attachShader(shaders.createShader(m_shaderType).getShader()); } }; class ProgramBinaryPersistenceRecompileShaderCase : public ProgramBinaryPersistenceCase { public: ProgramBinaryPersistenceRecompileShaderCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryPersistenceCase (context, name, desc, shaderType) { } virtual ~ProgramBinaryPersistenceRecompileShaderCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Recompiling " + std::string(getShaderTypeName(m_shaderType)) + " shader" << TestLog::EndMessage; caseShader.compile(); DE_UNREF(program); } }; class ProgramBinaryPersistenceReplaceSourceCase : public ProgramBinaryPersistenceCase { public: ProgramBinaryPersistenceReplaceSourceCase (Context& context, const char* name, const char* desc, glu::ShaderType shaderType) : ProgramBinaryPersistenceCase (context, name, desc, shaderType) { } virtual ~ProgramBinaryPersistenceReplaceSourceCase (void) { } void executeForProgram (glu::Program& program, ShaderAllocator& shaders) { TestLog& log = m_testCtx.getLog(); glu::Shader& caseShader = shaders.get(m_shaderType); log << TestLog::Message << "Replacing " + std::string(getShaderTypeName(m_shaderType)) + " shader source and recompiling" << TestLog::EndMessage; shaders.setSource(m_shaderType); caseShader.compile(); DE_UNREF(program); } }; // Test group ShaderApiTests::ShaderApiTests (Context& context) : TestCaseGroup(context, "shader_api", "Shader API Cases") { } ShaderApiTests::~ShaderApiTests (void) { } void ShaderApiTests::init (void) { // create and delete shaders { TestCaseGroup* createDeleteGroup = new TestCaseGroup(m_context, "create_delete", "glCreateShader() tests"); addChild(createDeleteGroup); createDeleteGroup->addChild(new CreateShaderCase(m_context, "create_vertex_shader", "Create vertex shader object", glu::SHADERTYPE_VERTEX)); createDeleteGroup->addChild(new CreateShaderCase(m_context, "create_fragment_shader", "Create fragment shader object", glu::SHADERTYPE_FRAGMENT)); createDeleteGroup->addChild(new DeleteShaderCase(m_context, "delete_vertex_fragment", "Delete vertex shader and fragment shader")); } // compile and link { TestCaseGroup* compileLinkGroup = new TestCaseGroup(m_context, "compile_link", "Compile and link tests"); addChild(compileLinkGroup); compileLinkGroup->addChild(new CompileShaderCase(m_context, "compile_vertex_shader", "Compile vertex shader", glu::SHADERTYPE_VERTEX)); compileLinkGroup->addChild(new CompileShaderCase(m_context, "compile_fragment_shader", "Compile fragment shader", glu::SHADERTYPE_FRAGMENT)); compileLinkGroup->addChild(new LinkVertexFragmentCase(m_context, "link_vertex_fragment", "Link vertex and fragment shaders")); } // shader source { TestCaseGroup* shaderSourceGroup = new TestCaseGroup(m_context, "shader_source", "glShaderSource() tests"); addChild(shaderSourceGroup); for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++) { const glu::ShaderType shaderType = (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX; const std::string shaderTypeName = getShaderTypeName(shaderType); const std::string caseName = std::string("replace_source_") + shaderTypeName; const std::string caseDesc = std::string("Replace source code of ") + shaderTypeName + " shader."; shaderSourceGroup->addChild(new ShaderSourceReplaceCase(m_context, caseName.c_str(), caseDesc.c_str(), shaderType)); } for (int stringLengthsInt = 0; stringLengthsInt < 3; stringLengthsInt++) for (int caseNdx = 1; caseNdx <= 3; caseNdx++) for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++) { const int numSlices = 1 << caseNdx; const glu::ShaderType shaderType = (shaderTypeInt == 1) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX; const bool explicitLengths = (stringLengthsInt != 0); const bool randomNullTerm = (stringLengthsInt == 2); const deUint32 flags = (explicitLengths ? CASE_EXPLICIT_SOURCE_LENGTHS : 0) | (randomNullTerm ? CASE_RANDOM_NULL_TERMINATED : 0); const std::string caseName = "split_source_" + de::toString(numSlices) + (randomNullTerm ? "_random_negative_length" : (explicitLengths ? "_specify_lengths" : "_null_terminated")) + ((shaderType == glu::SHADERTYPE_FRAGMENT) ? "_fragment" : "_vertex"); const std::string caseDesc = std::string((shaderType == glu::SHADERTYPE_FRAGMENT) ? "Fragment" : "Vertex") + " shader source split into " + de::toString(numSlices) + " pieces" + (explicitLengths ? ", using explicitly specified string lengths" : "") + (randomNullTerm ? " with random negative length values" : ""); shaderSourceGroup->addChild(new ShaderSourceSplitCase(m_context, caseName.c_str(), caseDesc.c_str(), shaderType, numSlices, flags)); } } // link status and infolog { TestCaseGroup* linkStatusGroup = new TestCaseGroup(m_context, "program_state", "Program state persistence tests"); addChild(linkStatusGroup); addProgramStateCase<ProgramStateDetachShaderCase> (linkStatusGroup, m_context, "detach_shader", "detach shader"); addProgramStateCase<ProgramStateReattachShaderCase> (linkStatusGroup, m_context, "reattach_shader", "reattach shader"); addProgramStateCase<ProgramStateDeleteShaderCase> (linkStatusGroup, m_context, "delete_shader", "delete shader"); addProgramStateCase<ProgramStateReplaceShaderCase> (linkStatusGroup, m_context, "replace_shader", "replace shader object"); addProgramStateCase<ProgramStateRecompileShaderCase> (linkStatusGroup, m_context, "recompile_shader", "recompile shader"); addProgramStateCase<ProgramStateReplaceSourceCase> (linkStatusGroup, m_context, "replace_source", "replace shader source"); } // program binary { TestCaseGroup* programBinaryGroup = new TestCaseGroup(m_context, "program_binary", "Program binary API tests"); addChild(programBinaryGroup); { TestCaseGroup* simpleCaseGroup = new TestCaseGroup(m_context, "simple", "Simple API tests"); programBinaryGroup->addChild(simpleCaseGroup); simpleCaseGroup->addChild(new ProgramBinarySimpleCase (m_context, "get_program_binary_vertex_fragment", "Get vertex and fragment shader program binary")); simpleCaseGroup->addChild(new ProgramBinaryUniformResetCase (m_context, "uniform_reset_on_binary_load", "Verify uniform reset on successful load of program binary")); } { TestCaseGroup* binaryPersistenceGroup = new TestCaseGroup(m_context, "binary_persistence", "Program binary persistence tests"); programBinaryGroup->addChild(binaryPersistenceGroup); addProgramBinaryPersistenceCase<ProgramBinaryPersistenceDetachShaderCase> (binaryPersistenceGroup, m_context, "detach_shader", "detach shader"); addProgramBinaryPersistenceCase<ProgramBinaryPersistenceReattachShaderCase> (binaryPersistenceGroup, m_context, "reattach_shader", "reattach shader"); addProgramBinaryPersistenceCase<ProgramBinaryPersistenceDeleteShaderCase> (binaryPersistenceGroup, m_context, "delete_shader", "delete shader"); addProgramBinaryPersistenceCase<ProgramBinaryPersistenceReplaceShaderCase> (binaryPersistenceGroup, m_context, "replace_shader", "replace shader object"); addProgramBinaryPersistenceCase<ProgramBinaryPersistenceRecompileShaderCase> (binaryPersistenceGroup, m_context, "recompile_shader", "recompile shader"); addProgramBinaryPersistenceCase<ProgramBinaryPersistenceReplaceSourceCase> (binaryPersistenceGroup, m_context, "replace_source", "replace shader source"); } } } } // Functional } // gles3 } // deqp