blob: 8e13e893e3c4827f6fd66aac15692c262d1775f8 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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(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, uint32_t 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, uint32_t 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 uint16_t 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;
}
} // namespace
// 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 uint32_t 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));
}
}
} // namespace
// 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<uint8_t> 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());
}
} // namespace
// 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 uint32_t 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 int32_t 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));
}
}
} // namespace
// 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 uint32_t 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");
}
}
}
} // namespace Functional
} // namespace gles3
} // namespace deqp