/*-------------------------------------------------------------------------
* 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 control statement performance tests.
*//*--------------------------------------------------------------------*/
#include "es3pShaderControlStatementTests.hpp"
#include "glsShaderPerformanceCase.hpp"
#include "tcuTestLog.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include <string>
#include <vector>
namespace deqp
{
namespace gles3
{
namespace Performance
{
using namespace gls;
using namespace glw; // GL types
using tcu::Vec4;
using tcu::TestLog;
using std::string;
using std::vector;
// Writes the workload expression used in conditional tests.
static void writeConditionalWorkload (std::ostringstream& stream, const char* resultName, const char* operandName)
{
const int numMultiplications = 64;
stream << resultName << " = ";
for (int i = 0; i < numMultiplications; i++)
{
if (i > 0)
stream << "*";
stream << operandName;
}
stream << ";";
}
// Writes the workload expression used in loop tests (one iteration).
static void writeLoopWorkload (std::ostringstream& stream, const char* resultName, const char* operandName)
{
const int numMultiplications = 8;
stream << resultName << " = ";
for (int i = 0; i < numMultiplications; i++)
{
if (i > 0)
stream << " * ";
stream << "(" << resultName << " + " << operandName << ")";
}
stream << ";";
}
// The type of decision to be made in a conditional expression.
// \note In fragment cases with DECISION_ATTRIBUTE, the value in the expression will actually be a varying.
enum DecisionType
{
DECISION_STATIC = 0,
DECISION_UNIFORM,
DECISION_ATTRIBUTE,
DECISION_LAST
};
class ControlStatementCase : public ShaderPerformanceCase
{
public:
ControlStatementCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, gls::PerfCaseType caseType)
: ShaderPerformanceCase(testCtx, renderCtx, name, description, caseType)
{
}
void init (void)
{
m_testCtx.getLog() << TestLog::Message << "Using additive blending." << TestLog::EndMessage;
ShaderPerformanceCase::init();
}
void setupRenderState (void)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
gl.enable(GL_BLEND);
gl.blendEquation(GL_FUNC_ADD);
gl.blendFunc(GL_ONE, GL_ONE);
}
};
class ConditionalCase : public ControlStatementCase
{
public:
enum BranchResult
{
BRANCH_TRUE = 0,
BRANCH_FALSE,
BRANCH_MIXED,
BRANCH_LAST
};
enum WorkloadDivision
{
WORKLOAD_DIVISION_EVEN = 0, //! Both true and false branches contain same amount of computation.
WORKLOAD_DIVISION_TRUE_HEAVY, //! True branch contains more computation.
WORKLOAD_DIVISION_FALSE_HEAVY, //! False branch contains more computation.
WORKLOAD_DIVISION_LAST
};
ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex);
~ConditionalCase (void);
void init (void);
void deinit (void);
void setupProgram (deUint32 program);
private:
DecisionType m_decisionType;
BranchResult m_branchType;
WorkloadDivision m_workloadDivision;
vector<float> m_comparisonValueArray; // Will contain per-vertex comparison values if using mixed branch type in vertex case.
deUint32 m_arrayBuffer;
};
ConditionalCase::ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex)
: ControlStatementCase (context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
, m_decisionType (decisionType)
, m_branchType (branchType)
, m_workloadDivision (workloadDivision)
, m_arrayBuffer (0)
{
}
void ConditionalCase::init (void)
{
bool isVertexCase = m_caseType == CASETYPE_VERTEX;
bool isStaticCase = m_decisionType == DECISION_STATIC;
bool isUniformCase = m_decisionType == DECISION_UNIFORM;
bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE;
DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase);
bool isConditionTrue = m_branchType == BRANCH_TRUE;
bool isConditionFalse = m_branchType == BRANCH_FALSE;
bool isConditionMixed = m_branchType == BRANCH_MIXED;
DE_ASSERT(isConditionTrue || isConditionFalse || isConditionMixed);
DE_UNREF(isConditionFalse);
DE_ASSERT(isAttributeCase || !isConditionMixed); // The branch taken can vary between executions only if using attribute input.
const char* staticCompareValueStr = isConditionTrue ? "1.0" : "-1.0";
const char* compareValueStr = isStaticCase ? staticCompareValueStr :
isUniformCase ? "u_compareValue" :
isVertexCase ? "a_compareValue" :
"v_compareValue";
std::ostringstream vtx;
std::ostringstream frag;
std::ostringstream& op = isVertexCase ? vtx : frag;
vtx << "#version 300 es\n";
vtx << "in highp vec4 a_position;\n"; // Position attribute.
vtx << "in mediump vec4 a_value0;\n"; // Input for workload calculations of "true" branch.
vtx << "in mediump vec4 a_value1;\n"; // Input for workload calculations of "false" branch.
frag << "#version 300 es\n";
frag << "layout(location = 0) out mediump vec4 o_color;\n";
// Value to be used in the conditional expression.
if (isAttributeCase)
vtx << "in mediump float a_compareValue;\n";
else if (isUniformCase)
op << "uniform mediump float u_compareValue;\n";
// Varyings.
if (isVertexCase)
{
vtx << "out mediump vec4 v_color;\n";
frag << "in mediump vec4 v_color;\n";
}
else
{
vtx << "out mediump vec4 v_value0;\n";
vtx << "out mediump vec4 v_value1;\n";
frag << "in mediump vec4 v_value0;\n";
frag << "in mediump vec4 v_value1;\n";
if (isAttributeCase)
{
vtx << "out mediump float v_compareValue;\n";
frag << "in mediump float v_compareValue;\n";
}
}
vtx << "\n";
vtx << "void main()\n";
vtx << "{\n";
vtx << " gl_Position = a_position;\n";
frag << "\n";
frag << "void main()\n";
frag << "{\n";
op << " mediump vec4 res;\n";
string condition;
if (isConditionMixed && !isVertexCase)
condition = string("") + "fract(" + compareValueStr + ") < 0.5"; // Comparison result varies with high frequency.
else
condition = string("") + compareValueStr + " > 0.0";
op << " if (" << condition << ")\n";
op << " {\n";
op << "\t\t";
if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_TRUE_HEAVY)
writeConditionalWorkload(op, "res", isVertexCase ? "a_value0" : "v_value0"); // Workload calculation for the "true" branch.
else
op << "res = " << (isVertexCase ? "a_value0" : "v_value0") << ";";
op << "\n";
op << " }\n";
op << " else\n";
op << " {\n";
op << "\t\t";
if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_FALSE_HEAVY)
writeConditionalWorkload(op, "res", isVertexCase ? "a_value1" : "v_value1"); // Workload calculations for the "false" branch.
else
op << "res = " << (isVertexCase ? "a_value1" : "v_value1") << ";";
op << "\n";
op << " }\n";
if (isVertexCase)
{
// Put result to color variable.
vtx << " v_color = res;\n";
frag << " o_color = v_color;\n";
}
else
{
// Transfer inputs to fragment shader through varyings.
if (isAttributeCase)
vtx << " v_compareValue = a_compareValue;\n";
vtx << " v_value0 = a_value0;\n";
vtx << " v_value1 = a_value1;\n";
frag << " o_color = res;\n"; // Put result to color variable.
}
vtx << "}\n";
frag << "}\n";
m_vertShaderSource = vtx.str();
m_fragShaderSource = frag.str();
if (isAttributeCase)
{
if (!isConditionMixed)
{
// Every execution takes the same branch.
float value = isConditionTrue ? +1.0f : -1.0f;
m_attributes.push_back(AttribSpec("a_compareValue", Vec4(value, 0.0f, 0.0f, 0.0f),
Vec4(value, 0.0f, 0.0f, 0.0f),
Vec4(value, 0.0f, 0.0f, 0.0f),
Vec4(value, 0.0f, 0.0f, 0.0f)));
}
else if (isVertexCase)
{
// Vertex case, not every execution takes the same branch.
const int numComponents = 4;
int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1);
// setupProgram() will later bind this array as an attribute.
m_comparisonValueArray.resize(numVertices * numComponents);
// Make every second vertex take the true branch, and every second the false branch.
for (int i = 0; i < (int)m_comparisonValueArray.size(); i++)
{
if (i % numComponents == 0)
m_comparisonValueArray[i] = (i / numComponents) % 2 == 0 ? +1.0f : -1.0f;
else
m_comparisonValueArray[i] = 0.0f;
}
}
else // isConditionMixed && !isVertexCase
{
// Fragment case, not every execution takes the same branch.
// \note fract(a_compareValue) < 0.5 will be true for every second column of fragments.
float minValue = 0.0f;
float maxValue = (float)getViewportWidth()*0.5f;
m_attributes.push_back(AttribSpec("a_compareValue", Vec4(minValue, 0.0f, 0.0f, 0.0f),
Vec4(maxValue, 0.0f, 0.0f, 0.0f),
Vec4(minValue, 0.0f, 0.0f, 0.0f),
Vec4(maxValue, 0.0f, 0.0f, 0.0f)));
}
}
m_attributes.push_back(AttribSpec("a_value0", Vec4(0.0f, 0.1f, 0.2f, 0.3f),
Vec4(0.4f, 0.5f, 0.6f, 0.7f),
Vec4(0.8f, 0.9f, 1.0f, 1.1f),
Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
m_attributes.push_back(AttribSpec("a_value1", Vec4(0.0f, 0.1f, 0.2f, 0.3f),
Vec4(0.4f, 0.5f, 0.6f, 0.7f),
Vec4(0.8f, 0.9f, 1.0f, 1.1f),
Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
ControlStatementCase::init();
}
void ConditionalCase::setupProgram (deUint32 program)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
if (m_decisionType == DECISION_UNIFORM)
{
int location = gl.getUniformLocation(program, "u_compareValue");
gl.uniform1f(location, m_branchType == BRANCH_TRUE ? +1.0f : -1.0f);
}
else if (m_decisionType == DECISION_ATTRIBUTE && m_branchType == BRANCH_MIXED && m_caseType == CASETYPE_VERTEX)
{
// Setup per-vertex comparison values calculated in init().
const int numComponents = 4;
int compareAttribLocation = gl.getAttribLocation(program, "a_compareValue");
DE_ASSERT((int)m_comparisonValueArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1));
gl.genBuffers(1, &m_arrayBuffer);
gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer);
gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_comparisonValueArray.size()*sizeof(float)), &m_comparisonValueArray[0], GL_STATIC_DRAW);
gl.enableVertexAttribArray(compareAttribLocation);
gl.vertexAttribPointer(compareAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL);
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
}
ConditionalCase::~ConditionalCase (void)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
if (m_arrayBuffer != 0)
{
gl.deleteBuffers(1, &m_arrayBuffer);
m_arrayBuffer = 0;
}
}
void ConditionalCase::deinit (void)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
m_comparisonValueArray.clear();
if (m_arrayBuffer != 0)
{
gl.deleteBuffers(1, &m_arrayBuffer);
m_arrayBuffer = 0;
}
ShaderPerformanceCase::deinit();
}
class LoopCase : public ControlStatementCase
{
public:
enum LoopType
{
LOOP_FOR = 0,
LOOP_WHILE,
LOOP_DO_WHILE,
LOOP_LAST
};
LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex);
~LoopCase (void);
void init (void);
void deinit (void);
void setupProgram (deUint32 program);
private:
DecisionType m_decisionType;
LoopType m_type;
bool m_isLoopBoundStable; // Whether loop bound is same in all executions.
vector<float> m_boundArray; // Will contain per-vertex loop bounds if using non-stable attribute in vertex case.
deUint32 m_arrayBuffer;
};
LoopCase::LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex)
: ControlStatementCase (context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
, m_decisionType (decisionType)
, m_type (type)
, m_isLoopBoundStable (isLoopBoundStable)
, m_arrayBuffer (0)
{
}
void LoopCase::init (void)
{
bool isVertexCase = m_caseType == CASETYPE_VERTEX;
bool isStaticCase = m_decisionType == DECISION_STATIC;
bool isUniformCase = m_decisionType == DECISION_UNIFORM;
bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE;
DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase);
DE_ASSERT(m_type == LOOP_FOR ||
m_type == LOOP_WHILE ||
m_type == LOOP_DO_WHILE);
DE_ASSERT(isAttributeCase || m_isLoopBoundStable); // The loop bound count can vary between executions only if using attribute input.
// \note The fractional part is .5 (instead of .0) so that these can be safely used as loop bounds.
const float loopBound = 10.5f;
const float unstableBoundLow = 5.5f;
const float unstableBoundHigh = 15.5f;
static const char* loopBoundStr = "10.5";
static const char* unstableBoundLowStr = "5.5";
static const char* unstableBoundHighStr = "15.5";
const char* boundValueStr = isStaticCase ? loopBoundStr :
isUniformCase ? "u_bound" :
isVertexCase ? "a_bound" :
m_isLoopBoundStable ? "v_bound" :
"loopBound";
std::ostringstream vtx;
std::ostringstream frag;
std::ostringstream& op = isVertexCase ? vtx : frag;
vtx << "#version 300 es\n";
vtx << "in highp vec4 a_position;\n"; // Position attribute.
vtx << "in mediump vec4 a_value;\n"; // Input for workload calculations.
frag << "#version 300 es\n";
frag << "layout(location = 0) out mediump vec4 o_color;\n";
// Value to be used as the loop iteration count.
if (isAttributeCase)
vtx << "in mediump float a_bound;\n";
else if (isUniformCase)
op << "uniform mediump float u_bound;\n";
// Varyings.
if (isVertexCase)
{
vtx << "out mediump vec4 v_color;\n";
frag << "in mediump vec4 v_color;\n";
}
else
{
vtx << "out mediump vec4 v_value;\n";
frag << "in mediump vec4 v_value;\n";
if (isAttributeCase)
{
vtx << "out mediump float v_bound;\n";
frag << "in mediump float v_bound;\n";
}
}
vtx << "\n";
vtx << "void main()\n";
vtx << "{\n";
vtx << " gl_Position = a_position;\n";
frag << "\n";
frag << "void main()\n";
frag << "{\n";
op << " mediump vec4 res = vec4(0.0);\n";
if (!m_isLoopBoundStable && !isVertexCase)
{
// Choose the actual loop bound based on v_bound.
// \note Loop bound will vary with high frequency between fragment columns, given appropriate range for v_bound.
op << " mediump float loopBound = fract(v_bound) < 0.5 ? " << unstableBoundLowStr << " : " << unstableBoundHighStr << ";\n";
}
// Start a for, while or do-while loop.
if (m_type == LOOP_FOR)
op << " for (mediump float i = 0.0; i < " << boundValueStr << "; i++)\n";
else
{
op << " mediump float i = 0.0;\n";
if (m_type == LOOP_WHILE)
op << " while (i < " << boundValueStr << ")\n";
else // LOOP_DO_WHILE
op << " do\n";
}
op << " {\n";
// Workload calculations inside the loop.
op << "\t\t";
writeLoopWorkload(op, "res", isVertexCase ? "a_value" : "v_value");
op << "\n";
// Only "for" has counter increment in the loop head.
if (m_type != LOOP_FOR)
op << " i++;\n";
// End the loop.
if (m_type == LOOP_DO_WHILE)
op << " } while (i < " << boundValueStr << ");\n";
else
op << " }\n";
if (isVertexCase)
{
// Put result to color variable.
vtx << " v_color = res;\n";
frag << " o_color = v_color;\n";
}
else
{
// Transfer inputs to fragment shader through varyings.
if (isAttributeCase)
vtx << " v_bound = a_bound;\n";
vtx << " v_value = a_value;\n";
frag << " o_color = res;\n"; // Put result to color variable.
}
vtx << "}\n";
frag << "}\n";
m_vertShaderSource = vtx.str();
m_fragShaderSource = frag.str();
if (isAttributeCase)
{
if (m_isLoopBoundStable)
{
// Every execution has same number of iterations.
m_attributes.push_back(AttribSpec("a_bound", Vec4(loopBound, 0.0f, 0.0f, 0.0f),
Vec4(loopBound, 0.0f, 0.0f, 0.0f),
Vec4(loopBound, 0.0f, 0.0f, 0.0f),
Vec4(loopBound, 0.0f, 0.0f, 0.0f)));
}
else if (isVertexCase)
{
// Vertex case, with non-constant number of iterations.
const int numComponents = 4;
int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1);
// setupProgram() will later bind this array as an attribute.
m_boundArray.resize(numVertices * numComponents);
// Vary between low and high loop bounds; they should average to loopBound however.
for (int i = 0; i < (int)m_boundArray.size(); i++)
{
if (i % numComponents == 0)
m_boundArray[i] = (i / numComponents) % 2 == 0 ? unstableBoundLow : unstableBoundHigh;
else
m_boundArray[i] = 0.0f;
}
}
else // !m_isLoopBoundStable && !isVertexCase
{
// Fragment case, with non-constant number of iterations.
// \note fract(a_bound) < 0.5 will be true for every second fragment.
float minValue = 0.0f;
float maxValue = (float)getViewportWidth()*0.5f;
m_attributes.push_back(AttribSpec("a_bound", Vec4(minValue, 0.0f, 0.0f, 0.0f),
Vec4(maxValue, 0.0f, 0.0f, 0.0f),
Vec4(minValue, 0.0f, 0.0f, 0.0f),
Vec4(maxValue, 0.0f, 0.0f, 0.0f)));
}
}
m_attributes.push_back(AttribSpec("a_value", Vec4(0.0f, 0.1f, 0.2f, 0.3f),
Vec4(0.4f, 0.5f, 0.6f, 0.7f),
Vec4(0.8f, 0.9f, 1.0f, 1.1f),
Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
ControlStatementCase::init();
}
void LoopCase::setupProgram (deUint32 program)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
if (m_decisionType == DECISION_UNIFORM)
{
const float loopBound = 10.5f;
int location = gl.getUniformLocation(program, "u_bound");
gl.uniform1f(location, loopBound);
}
else if (m_decisionType == DECISION_ATTRIBUTE && !m_isLoopBoundStable && m_caseType == CASETYPE_VERTEX)
{
// Setup per-vertex loop bounds calculated in init().
const int numComponents = 4;
int boundAttribLocation = gl.getAttribLocation(program, "a_bound");
DE_ASSERT((int)m_boundArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1));
gl.genBuffers(1, &m_arrayBuffer);
gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer);
gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_boundArray.size()*sizeof(float)), &m_boundArray[0], GL_STATIC_DRAW);
gl.enableVertexAttribArray(boundAttribLocation);
gl.vertexAttribPointer(boundAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL);
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
}
LoopCase::~LoopCase (void)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
if (m_arrayBuffer)
{
gl.deleteBuffers(1, &m_arrayBuffer);
m_arrayBuffer = 0;
}
}
void LoopCase::deinit (void)
{
const glw::Functions& gl = m_renderCtx.getFunctions();
m_boundArray.clear();
if (m_arrayBuffer)
{
gl.deleteBuffers(1, &m_arrayBuffer);
m_arrayBuffer = 0;
}
ShaderPerformanceCase::deinit();
}
// A reference case, same calculations as the actual tests but without control statements.
class WorkloadReferenceCase : public ControlStatementCase
{
public:
WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex);
void init (void);
protected:
virtual void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const = 0;
};
WorkloadReferenceCase::WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex)
: ControlStatementCase(context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
{
}
void WorkloadReferenceCase::init (void)
{
bool isVertexCase = m_caseType == CASETYPE_VERTEX;
std::ostringstream vtx;
std::ostringstream frag;
std::ostringstream& op = isVertexCase ? vtx : frag;
vtx << "#version 300 es\n";
vtx << "in highp vec4 a_position;\n"; // Position attribute.
vtx << "in mediump vec4 a_value;\n"; // Value for workload calculations.
frag << "#version 300 es\n";
frag << "layout(location = 0) out mediump vec4 o_color;\n";
// Varyings.
if (isVertexCase)
{
vtx << "out mediump vec4 v_color;\n";
frag << "in mediump vec4 v_color;\n";
}
else
{
vtx << "out mediump vec4 v_value;\n";
frag << "in mediump vec4 v_value;\n";
}
vtx << "\n";
vtx << "void main()\n";
vtx << "{\n";
vtx << " gl_Position = a_position;\n";
frag << "\n";
frag << "void main()\n";
frag << "{\n";
op << "\tmediump vec4 res;\n";
writeWorkload(op, "res", isVertexCase ? "a_value" : "v_value");
if (isVertexCase)
{
// Put result to color variable.
vtx << " v_color = res;\n";
frag << " o_color = v_color;\n";
}
else
{
vtx << " v_value = a_value;\n"; // Transfer input to fragment shader through varying.
frag << " o_color = res;\n"; // Put result to color variable.
}
vtx << "}\n";
frag << "}\n";
m_vertShaderSource = vtx.str();
m_fragShaderSource = frag.str();
m_attributes.push_back(AttribSpec("a_value", Vec4(0.0f, 0.1f, 0.2f, 0.3f),
Vec4(0.4f, 0.5f, 0.6f, 0.7f),
Vec4(0.8f, 0.9f, 1.0f, 1.1f),
Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
ControlStatementCase::init();
}
class LoopWorkloadReferenceCase : public WorkloadReferenceCase
{
public:
LoopWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex)
: WorkloadReferenceCase (context, name, description, isVertex)
, m_isAttributeStable (isAttributeStable)
{
}
protected:
void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
private:
bool m_isAttributeStable;
};
void LoopWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
{
const int loopIterations = 11;
bool isVertexCase = m_caseType == CASETYPE_VERTEX;
dst << "\t" << resultVariableName << " = vec4(0.0);\n";
for (int i = 0; i < loopIterations; i++)
{
dst << "\t";
writeLoopWorkload(dst, resultVariableName, inputVariableName);
dst << "\n";
}
if (!isVertexCase && !m_isAttributeStable)
{
// Corresponds to the fract() done in a real test's fragment case with non-stable attribute.
dst << " res.x = fract(res.x);\n";
}
}
class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase
{
public:
ConditionalWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex)
: WorkloadReferenceCase (context, name, description, isVertex)
, m_isAttributeStable (isAttributeStable)
{
}
protected:
void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
private:
bool m_isAttributeStable;
};
void ConditionalWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
{
bool isVertexCase = m_caseType == CASETYPE_VERTEX;
dst << "\t";
writeConditionalWorkload(dst, resultVariableName, inputVariableName);
dst << "\n";
if (!isVertexCase && !m_isAttributeStable)
{
// Corresponds to the fract() done in a real test's fragment case with non-stable attribute.
dst << " res.x = fract(res.x);\n";
}
}
// A workload reference case for e.g. a conditional case with a branch with no computation.
class EmptyWorkloadReferenceCase : public WorkloadReferenceCase
{
public:
EmptyWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex)
: WorkloadReferenceCase (context, name, description, isVertex)
{
}
protected:
void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
{
dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
}
};
ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
: TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
{
}
ShaderControlStatementTests::~ShaderControlStatementTests (void)
{
}
void ShaderControlStatementTests::init (void)
{
// Conditional cases (if-else).
tcu::TestCaseGroup* ifElseGroup = new tcu::TestCaseGroup(m_testCtx, "if_else", "if-else Conditional Performance Tests");
addChild(ifElseGroup);
for (int isFrag = 0; isFrag <= 1; isFrag++)
{
bool isVertex = isFrag == 0;
ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
ifElseGroup->addChild(vertexOrFragmentGroup);
DE_STATIC_ASSERT(DECISION_STATIC == 0);
for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
{
const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" :
decisionType == (int)DECISION_UNIFORM ? "uniform" :
decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") :
DE_NULL;
DE_ASSERT(decisionName != DE_NULL);
for (int workloadDivision = 0; workloadDivision < ConditionalCase::WORKLOAD_DIVISION_LAST; workloadDivision++)
{
const char* workloadDivisionSuffix = workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_EVEN ? "" :
workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_TRUE_HEAVY ? "_with_heavier_true" :
workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_FALSE_HEAVY ? "_with_heavier_false" :
DE_NULL;
DE_ASSERT(workloadDivisionSuffix != DE_NULL);
DE_STATIC_ASSERT(ConditionalCase::BRANCH_TRUE == 0);
for (int branchResult = (int)ConditionalCase::BRANCH_TRUE; branchResult < (int)ConditionalCase::BRANCH_LAST; branchResult++)
{
if (decisionType != (int)DECISION_ATTRIBUTE && branchResult == (int)ConditionalCase::BRANCH_MIXED)
continue;
const char* branchResultName = branchResult == (int)ConditionalCase::BRANCH_TRUE ? "true" :
branchResult == (int)ConditionalCase::BRANCH_FALSE ? "false" :
branchResult == (int)ConditionalCase::BRANCH_MIXED ? "mixed" :
DE_NULL;
DE_ASSERT(branchResultName != DE_NULL);
string caseName = string("") + decisionName + "_" + branchResultName + workloadDivisionSuffix;
vertexOrFragmentGroup->addChild(new ConditionalCase(m_context, caseName.c_str(), "",
(DecisionType)decisionType, (ConditionalCase::BranchResult)branchResult,
(ConditionalCase::WorkloadDivision)workloadDivision, isVertex));
}
}
}
if (isVertex)
vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
else
{
// Only fragment case with BRANCH_MIXED has an additional fract() call.
vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_unmixed", "", true, isVertex));
vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_mixed", "", false, isVertex));
}
vertexOrFragmentGroup->addChild(new EmptyWorkloadReferenceCase(m_context, "reference_empty", "", isVertex));
}
// Loop cases.
static const struct
{
LoopCase::LoopType type;
const char* name;
const char* description;
} loopGroups[] =
{
{LoopCase::LOOP_FOR, "for", "for Loop Performance Tests"},
{LoopCase::LOOP_WHILE, "while", "while Loop Performance Tests"},
{LoopCase::LOOP_DO_WHILE, "do_while", "do-while Loop Performance Tests"}
};
for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(loopGroups); groupNdx++)
{
tcu::TestCaseGroup* currentLoopGroup = new tcu::TestCaseGroup(m_testCtx, loopGroups[groupNdx].name, loopGroups[groupNdx].description);
addChild(currentLoopGroup);
for (int isFrag = 0; isFrag <= 1; isFrag++)
{
bool isVertex = isFrag == 0;
ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
currentLoopGroup->addChild(vertexOrFragmentGroup);
DE_STATIC_ASSERT(DECISION_STATIC == 0);
for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
{
const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" :
decisionType == (int)DECISION_UNIFORM ? "uniform" :
decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") :
DE_NULL;
DE_ASSERT(decisionName != DE_NULL);
if (decisionType == (int)DECISION_ATTRIBUTE)
{
vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_stable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex));
vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_unstable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, false, isVertex));
}
else
vertexOrFragmentGroup->addChild(new LoopCase(m_context, decisionName, "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex));
}
if (isVertex)
vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
else
{
// Only fragment case with unstable attribute has an additional fract() call.
vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_stable", "", true, isVertex));
vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_unstable", "", false, isVertex));
}
}
}
}
} // Performance
} // gles3
} // deqp