You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
12 KiB
473 lines
12 KiB
/*-------------------------------------------------------------------------
|
|
* 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 Long shader compilation stress tests
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es3sLongShaderTests.hpp"
|
|
|
|
#include "deRandom.hpp"
|
|
#include "deStringUtil.hpp"
|
|
#include "deString.h"
|
|
#include "tcuTestLog.hpp"
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "glwFunctions.hpp"
|
|
#include "glwEnums.hpp"
|
|
|
|
#include <string>
|
|
#include <set>
|
|
#include <map>
|
|
#include <cmath>
|
|
|
|
using tcu::TestLog;
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gles3
|
|
{
|
|
namespace Stress
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
enum LongShaderCaseFlags
|
|
{
|
|
CASE_REQUIRE_LINK_STATUS_OK = 1
|
|
};
|
|
|
|
const char* getConstVertShaderSource (void)
|
|
{
|
|
const char* const src =
|
|
"#version 300 es\n"
|
|
"void main ()\n"
|
|
"{\n"
|
|
" gl_Position = vec4(0.0);\n"
|
|
"}\n";
|
|
|
|
return src;
|
|
}
|
|
|
|
const char* getConstFragShaderSource (void)
|
|
{
|
|
const char* const src =
|
|
"#version 300 es\n"
|
|
"layout(location = 0) out mediump vec4 o_fragColor;\n"
|
|
"void main ()\n"
|
|
"{\n"
|
|
" o_fragColor = vec4(0.0);\n"
|
|
"}\n";
|
|
|
|
return src;
|
|
}
|
|
|
|
const char* getConstShaderSource (const glu::ShaderType shaderType)
|
|
{
|
|
DE_ASSERT(shaderType == glu::SHADERTYPE_VERTEX || shaderType == glu::SHADERTYPE_FRAGMENT);
|
|
|
|
if (shaderType == glu::SHADERTYPE_VERTEX)
|
|
return getConstVertShaderSource();
|
|
else
|
|
return getConstFragShaderSource();
|
|
}
|
|
|
|
typedef std::set<std::string> ShaderScope;
|
|
|
|
const char variableNamePrefixChars[] = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
class NameGenerator
|
|
{
|
|
public:
|
|
NameGenerator (void)
|
|
: m_scopeIndices (1, 0)
|
|
, m_currentScopeDepth (1)
|
|
, m_variableIndex (0)
|
|
{
|
|
}
|
|
|
|
void beginScope (void)
|
|
{
|
|
m_currentScopeDepth++;
|
|
|
|
if (m_scopeIndices.size() < (size_t)m_currentScopeDepth)
|
|
m_scopeIndices.push_back(0);
|
|
else
|
|
m_scopeIndices[m_currentScopeDepth-1]++;
|
|
|
|
m_variableIndex = 0;
|
|
}
|
|
|
|
void endScope (void)
|
|
{
|
|
DE_ASSERT(m_currentScopeDepth > 1);
|
|
|
|
m_currentScopeDepth--;
|
|
}
|
|
|
|
std::string makePrefix (void)
|
|
{
|
|
std::string prefix;
|
|
|
|
for (int ndx = 0; ndx < m_currentScopeDepth; ndx++)
|
|
{
|
|
const int scopeIndex = m_scopeIndices[m_currentScopeDepth-1];
|
|
|
|
DE_ASSERT(scopeIndex < DE_LENGTH_OF_ARRAY(variableNamePrefixChars));
|
|
|
|
prefix += variableNamePrefixChars[scopeIndex];
|
|
}
|
|
|
|
return prefix;
|
|
}
|
|
|
|
std::string next (void)
|
|
{
|
|
m_variableIndex++;
|
|
|
|
return makePrefix() + de::toString(m_variableIndex);
|
|
}
|
|
|
|
void makeNames (ShaderScope& scope, const deUint32 count)
|
|
{
|
|
for (deUint32 ndx = 0; ndx < count; ndx++)
|
|
scope.insert(next());
|
|
}
|
|
|
|
private:
|
|
std::vector<int> m_scopeIndices;
|
|
int m_currentScopeDepth;
|
|
int m_variableIndex;
|
|
};
|
|
|
|
struct LongShaderSpec
|
|
{
|
|
glu::ShaderType shaderType;
|
|
deUint32 opsTotal;
|
|
|
|
deUint32 variablesPerBlock;
|
|
deUint32 opsPerExpression;
|
|
|
|
LongShaderSpec (const glu::ShaderType shaderTypeInit, const deUint32 opsTotalInit)
|
|
: shaderType (shaderTypeInit)
|
|
, opsTotal (opsTotalInit)
|
|
, variablesPerBlock (deMaxu32(10, (deUint32)std::floor(std::sqrt((double)opsTotal))))
|
|
, opsPerExpression (deMinu32(10, variablesPerBlock / 2))
|
|
{
|
|
}
|
|
};
|
|
|
|
// Generator for long test shaders
|
|
|
|
class LongShaderGenerator
|
|
{
|
|
public:
|
|
LongShaderGenerator (de::Random& rnd, const LongShaderSpec& spec);
|
|
|
|
glu::ShaderSource getSource (void);
|
|
|
|
private:
|
|
de::Random m_rnd;
|
|
const LongShaderSpec m_spec;
|
|
|
|
NameGenerator m_nameGen;
|
|
|
|
std::vector<std::string> m_varNames;
|
|
std::vector<ShaderScope> m_scopes;
|
|
|
|
std::string m_source;
|
|
|
|
void generateSource (void);
|
|
|
|
std::string getRandomVariableName (void);
|
|
std::string getShaderOutputName (void);
|
|
std::string makeExpression (const std::vector<std::string>& varNames, const int numOps);
|
|
|
|
void addIndent (void);
|
|
void addLine (const std::string& text);
|
|
|
|
void beginBlock (void);
|
|
void endBlock (void);
|
|
};
|
|
|
|
LongShaderGenerator::LongShaderGenerator (de::Random& rnd, const LongShaderSpec& spec)
|
|
: m_rnd (rnd)
|
|
, m_spec (spec)
|
|
{
|
|
DE_ASSERT(m_spec.shaderType == glu::SHADERTYPE_VERTEX || m_spec.shaderType == glu::SHADERTYPE_FRAGMENT);
|
|
}
|
|
|
|
glu::ShaderSource LongShaderGenerator::getSource (void)
|
|
{
|
|
if (m_source.empty())
|
|
generateSource();
|
|
|
|
return glu::ShaderSource(m_spec.shaderType, m_source);
|
|
}
|
|
|
|
void LongShaderGenerator::generateSource (void)
|
|
{
|
|
deUint32 currentOpsTotal = 0;
|
|
|
|
m_source.clear();
|
|
|
|
addLine("#version 300 es");
|
|
|
|
if (m_spec.shaderType == glu::SHADERTYPE_FRAGMENT)
|
|
addLine("layout(location = 0) out mediump vec4 o_fragColor;");
|
|
|
|
addLine("void main (void)");
|
|
beginBlock();
|
|
|
|
while (currentOpsTotal < m_spec.opsTotal)
|
|
{
|
|
const bool isLast = (m_spec.opsTotal <= (currentOpsTotal + m_spec.opsPerExpression));
|
|
const int numOps = isLast ? (m_spec.opsTotal - currentOpsTotal) : m_spec.opsPerExpression;
|
|
const size_t numVars = numOps + 1;
|
|
|
|
const std::string outName = isLast ? getShaderOutputName() : getRandomVariableName();
|
|
std::vector<std::string> inNames (numVars);
|
|
|
|
DE_ASSERT(numVars < m_varNames.size());
|
|
m_rnd.choose(m_varNames.begin(), m_varNames.end(), inNames.begin(), (int)numVars);
|
|
|
|
{
|
|
std::string expr = makeExpression(inNames, numOps);
|
|
|
|
if (isLast)
|
|
addLine(outName + " = vec4(" + expr + ");");
|
|
else
|
|
addLine(outName + " = " + expr + ";");
|
|
}
|
|
|
|
currentOpsTotal += numOps;
|
|
}
|
|
|
|
while (!m_scopes.empty())
|
|
endBlock();
|
|
}
|
|
|
|
std::string LongShaderGenerator::getRandomVariableName (void)
|
|
{
|
|
return m_rnd.choose<std::string>(m_varNames.begin(), m_varNames.end());
|
|
}
|
|
|
|
std::string LongShaderGenerator::getShaderOutputName (void)
|
|
{
|
|
return (m_spec.shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "o_fragColor";
|
|
}
|
|
|
|
std::string LongShaderGenerator::makeExpression (const std::vector<std::string>& varNames, const int numOps)
|
|
{
|
|
const std::string operators = "+-*/";
|
|
std::string expr;
|
|
|
|
DE_ASSERT(varNames.size() > (size_t)numOps);
|
|
|
|
expr = varNames[0];
|
|
|
|
for (int ndx = 1; ndx <= numOps; ndx++)
|
|
{
|
|
const std::string op = std::string("") + m_rnd.choose<char>(operators.begin(), operators.end());
|
|
const std::string varName = varNames[ndx];
|
|
|
|
expr += " " + op + " " + varName;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
|
|
void LongShaderGenerator::addIndent (void)
|
|
{
|
|
m_source += std::string(m_scopes.size(), '\t');
|
|
}
|
|
|
|
void LongShaderGenerator::addLine (const std::string& text)
|
|
{
|
|
addIndent();
|
|
m_source += text + "\n";
|
|
}
|
|
|
|
void LongShaderGenerator::beginBlock (void)
|
|
{
|
|
ShaderScope scope;
|
|
|
|
addLine("{");
|
|
|
|
m_nameGen.beginScope();
|
|
m_nameGen.makeNames(scope, m_spec.variablesPerBlock);
|
|
|
|
m_scopes.push_back(scope);
|
|
|
|
for (ShaderScope::const_iterator nameIter = scope.begin(); nameIter != scope.end(); nameIter++)
|
|
{
|
|
const std::string varName = *nameIter;
|
|
const float varValue = m_rnd.getFloat();
|
|
|
|
addLine("mediump float " + varName + " = " + de::floatToString(varValue, 5) + "f;");
|
|
m_varNames.push_back(varName);
|
|
}
|
|
}
|
|
|
|
void LongShaderGenerator::endBlock (void)
|
|
{
|
|
ShaderScope& scope = *(m_scopes.end()-1);
|
|
|
|
DE_ASSERT(!m_scopes.empty());
|
|
|
|
m_varNames.erase((m_varNames.begin() + (m_varNames.size() - scope.size())), m_varNames.end());
|
|
|
|
m_nameGen.endScope();
|
|
m_scopes.pop_back();
|
|
|
|
addLine("}");
|
|
}
|
|
|
|
} // anonymous
|
|
|
|
// Stress test case for compilation of large shaders
|
|
|
|
class LongShaderCompileStressCase : public TestCase
|
|
{
|
|
public:
|
|
LongShaderCompileStressCase (Context& context, const char* name, const char* desc, const LongShaderSpec& caseSpec, const deUint32 flags);
|
|
virtual ~LongShaderCompileStressCase (void);
|
|
|
|
void init (void);
|
|
|
|
IterateResult iterate (void);
|
|
|
|
void verify (const glu::ShaderProgram& program);
|
|
|
|
private:
|
|
const glu::ShaderType m_shaderType;
|
|
const deUint32 m_flags;
|
|
de::Random m_rnd;
|
|
LongShaderGenerator m_gen;
|
|
};
|
|
|
|
LongShaderCompileStressCase::LongShaderCompileStressCase (Context& context, const char* name, const char* desc, const LongShaderSpec& caseSpec, const deUint32 flags)
|
|
: TestCase (context, name, desc)
|
|
, m_shaderType (caseSpec.shaderType)
|
|
, m_flags (flags)
|
|
, m_rnd (deStringHash(name) ^ 0xac9c91d)
|
|
, m_gen (m_rnd, caseSpec)
|
|
{
|
|
DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT);
|
|
}
|
|
|
|
LongShaderCompileStressCase::~LongShaderCompileStressCase (void)
|
|
{
|
|
}
|
|
|
|
void LongShaderCompileStressCase::init (void)
|
|
{
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
|
}
|
|
|
|
tcu::TestCase::IterateResult LongShaderCompileStressCase::iterate (void)
|
|
{
|
|
tcu::TestLog& log = m_testCtx.getLog();
|
|
const glu::ShaderType otherShader = (m_shaderType == glu::SHADERTYPE_VERTEX) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX;
|
|
glu::ProgramSources sources;
|
|
|
|
sources << m_gen.getSource();
|
|
sources << glu::ShaderSource(otherShader, getConstShaderSource(otherShader));
|
|
|
|
{
|
|
glu::ShaderProgram program(m_context.getRenderContext(), sources);
|
|
|
|
verify(program);
|
|
|
|
log << program;
|
|
}
|
|
|
|
return STOP;
|
|
}
|
|
|
|
void LongShaderCompileStressCase::verify (const glu::ShaderProgram& program)
|
|
{
|
|
tcu::TestLog& log = m_testCtx.getLog();
|
|
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
|
|
const bool isStrict = (m_flags & CASE_REQUIRE_LINK_STATUS_OK) != 0;
|
|
const glw::GLenum errorCode = gl.getError();
|
|
|
|
if (isStrict && !program.isOk())
|
|
{
|
|
log << TestLog::Message << "Fail, expected program to compile and link successfully." << TestLog::EndMessage;
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Linking failed");
|
|
}
|
|
|
|
if (program.isOk() && (errorCode != GL_NO_ERROR))
|
|
{
|
|
log << TestLog::Message << "Fail, program status OK but a GL error was received (" << errorCode << ")." << TestLog::EndMessage;
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Conflicting status");
|
|
}
|
|
else if ((errorCode != GL_NO_ERROR) && (errorCode != GL_OUT_OF_MEMORY))
|
|
{
|
|
log << TestLog::Message << "Fail, expected GL_NO_ERROR or GL_OUT_OF_MEMORY, received " << errorCode << "." << TestLog::EndMessage;
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected GL error");
|
|
}
|
|
}
|
|
|
|
LongShaderTests::LongShaderTests (Context& testCtx)
|
|
: TestCaseGroup(testCtx, "long_shaders", "Long shader compilation stress tests")
|
|
{
|
|
}
|
|
|
|
LongShaderTests::~LongShaderTests(void)
|
|
{
|
|
}
|
|
|
|
void LongShaderTests::init (void)
|
|
{
|
|
const deUint32 requireLinkOkMaxOps = 1000;
|
|
|
|
const deUint32 caseOpCounts[] =
|
|
{
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000
|
|
};
|
|
|
|
for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(caseOpCounts); caseNdx++)
|
|
{
|
|
for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++)
|
|
{
|
|
const glu::ShaderType shaderType = (shaderTypeInt == 0) ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT;
|
|
const deUint32 opCount = caseOpCounts[caseNdx];
|
|
const deUint32 flags = (opCount <= requireLinkOkMaxOps) ? CASE_REQUIRE_LINK_STATUS_OK : 0;
|
|
|
|
const std::string name = de::toString(opCount) + "_operations_" + glu::getShaderTypeName(shaderType);
|
|
const std::string desc = std::string("Compile ") + glu::getShaderTypeName(shaderType) + " shader with " + de::toString(opCount) + " operations";
|
|
|
|
LongShaderSpec caseSpec (shaderType, opCount);
|
|
|
|
addChild(new LongShaderCompileStressCase(m_context, name.c_str(), desc.c_str(), caseSpec, flags));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // Stress
|
|
} // gles3
|
|
} // deqp
|