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.
1016 lines
41 KiB
1016 lines
41 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL ES 2.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 Optimized vs unoptimized shader performance tests.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es2pShaderOptimizationTests.hpp"
|
|
#include "glsShaderPerformanceMeasurer.hpp"
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuVector.hpp"
|
|
#include "tcuStringTemplate.hpp"
|
|
#include "deSharedPtr.hpp"
|
|
#include "deStringUtil.hpp"
|
|
#include "deMath.h"
|
|
|
|
#include "glwFunctions.hpp"
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
#include <map>
|
|
|
|
using glu::ShaderProgram;
|
|
using tcu::TestLog;
|
|
using tcu::Vec4;
|
|
using de::SharedPtr;
|
|
using de::toString;
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
|
|
namespace deqp
|
|
{
|
|
|
|
using gls::ShaderPerformanceMeasurer;
|
|
|
|
namespace gles2
|
|
{
|
|
namespace Performance
|
|
{
|
|
|
|
static inline std::map<string, string> singleMap (const string& key, const string& value)
|
|
{
|
|
std::map<string, string> res;
|
|
res[key] = value;
|
|
return res;
|
|
}
|
|
|
|
static inline string repeat (const string& str, int numRepeats, const string& delim = "")
|
|
{
|
|
string result = str;
|
|
for (int i = 1; i < numRepeats; i++)
|
|
result += delim + str;
|
|
return result;
|
|
}
|
|
|
|
static inline string repeatIndexedTemplate (const string& strTempl, int numRepeats, const string& delim = "", int ndxStart = 0)
|
|
{
|
|
const tcu::StringTemplate templ(strTempl);
|
|
string result;
|
|
std::map<string, string> params;
|
|
|
|
for (int i = 0; i < numRepeats; i++)
|
|
{
|
|
params["PREV_NDX"] = toString(i + ndxStart - 1);
|
|
params["NDX"] = toString(i + ndxStart);
|
|
|
|
result += (i > 0 ? delim : "") + templ.specialize(params);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
enum CaseShaderType
|
|
{
|
|
CASESHADERTYPE_VERTEX = 0,
|
|
CASESHADERTYPE_FRAGMENT,
|
|
|
|
CASESHADERTYPE_LAST
|
|
};
|
|
|
|
static inline string getShaderPrecision (CaseShaderType shaderType)
|
|
{
|
|
switch (shaderType)
|
|
{
|
|
case CASESHADERTYPE_VERTEX: return "highp";
|
|
case CASESHADERTYPE_FRAGMENT: return "mediump";
|
|
default:
|
|
DE_ASSERT(false);
|
|
return DE_NULL;
|
|
}
|
|
}
|
|
|
|
struct ProgramData
|
|
{
|
|
glu::ProgramSources sources;
|
|
vector<gls::AttribSpec> attributes; //!< \note Shouldn't contain a_position; that one is set by gls::ShaderPerformanceMeasurer.
|
|
|
|
ProgramData (void) {}
|
|
ProgramData (const glu::ProgramSources& sources_, const vector<gls::AttribSpec>& attributes_ = vector<gls::AttribSpec>()) : sources(sources_), attributes(attributes_) {}
|
|
ProgramData (const glu::ProgramSources& sources_, const gls::AttribSpec& attribute) : sources(sources_), attributes(1, attribute) {}
|
|
};
|
|
|
|
//! Shader boilerplate helper; most cases have similar basic shader structure.
|
|
static inline ProgramData defaultProgramData (CaseShaderType shaderType, const string& funcDefs, const string& mainStatements)
|
|
{
|
|
const bool isVertexCase = shaderType == CASESHADERTYPE_VERTEX;
|
|
const bool isFragmentCase = shaderType == CASESHADERTYPE_FRAGMENT;
|
|
const string vtxPrec = getShaderPrecision(CASESHADERTYPE_VERTEX);
|
|
const string fragPrec = getShaderPrecision(CASESHADERTYPE_FRAGMENT);
|
|
|
|
return ProgramData(glu::ProgramSources() << glu::VertexSource( "attribute " + vtxPrec + " vec4 a_position;\n"
|
|
"attribute " + vtxPrec + " vec4 a_value;\n"
|
|
"varying " + fragPrec + " vec4 v_value;\n"
|
|
+ (isVertexCase ? funcDefs : "") +
|
|
"void main (void)\n"
|
|
"{\n"
|
|
" gl_Position = a_position;\n"
|
|
" " + vtxPrec + " vec4 value = a_value;\n"
|
|
+ (isVertexCase ? mainStatements : "") +
|
|
" v_value = value;\n"
|
|
"}\n")
|
|
|
|
<< glu::FragmentSource( "varying " + fragPrec + " vec4 v_value;\n"
|
|
+ (isFragmentCase ? funcDefs : "") +
|
|
"void main (void)\n"
|
|
"{\n"
|
|
" " + fragPrec + " vec4 value = v_value;\n"
|
|
+ (isFragmentCase ? mainStatements : "") +
|
|
" gl_FragColor = value;\n"
|
|
"}\n"),
|
|
gls::AttribSpec("a_value",
|
|
Vec4(1.0f, 0.0f, 0.0f, 0.0f),
|
|
Vec4(0.0f, 1.0f, 0.0f, 0.0f),
|
|
Vec4(0.0f, 0.0f, 1.0f, 0.0f),
|
|
Vec4(0.0f, 0.0f, 0.0f, 1.0f)));
|
|
}
|
|
|
|
static inline ProgramData defaultProgramData (CaseShaderType shaderType, const string& mainStatements)
|
|
{
|
|
return defaultProgramData(shaderType, "", mainStatements);
|
|
}
|
|
|
|
class ShaderOptimizationCase : public TestCase
|
|
{
|
|
public:
|
|
ShaderOptimizationCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType)
|
|
: TestCase (context, tcu::NODETYPE_PERFORMANCE, name, description)
|
|
, m_caseShaderType (caseShaderType)
|
|
, m_state (STATE_LAST)
|
|
, m_measurer (context.getRenderContext(), caseShaderType == CASESHADERTYPE_VERTEX ? gls::CASETYPE_VERTEX
|
|
: caseShaderType == CASESHADERTYPE_FRAGMENT ? gls::CASETYPE_FRAGMENT
|
|
: gls::CASETYPE_LAST)
|
|
, m_unoptimizedResult (-1.0f, -1.0f)
|
|
, m_optimizedResult (-1.0f, -1.0f)
|
|
{
|
|
}
|
|
|
|
virtual ~ShaderOptimizationCase (void) {}
|
|
|
|
void init (void);
|
|
IterateResult iterate (void);
|
|
|
|
protected:
|
|
virtual ProgramData generateProgramData (bool optimized) const = 0;
|
|
|
|
const CaseShaderType m_caseShaderType;
|
|
|
|
private:
|
|
enum State
|
|
{
|
|
STATE_INIT_UNOPTIMIZED = 0,
|
|
STATE_MEASURE_UNOPTIMIZED,
|
|
STATE_INIT_OPTIMIZED,
|
|
STATE_MEASURE_OPTIMIZED,
|
|
STATE_FINISHED,
|
|
|
|
STATE_LAST
|
|
};
|
|
|
|
ProgramData& programData (bool optimized) { return optimized ? m_optimizedData : m_unoptimizedData; }
|
|
SharedPtr<const ShaderProgram>& program (bool optimized) { return optimized ? m_optimizedProgram : m_unoptimizedProgram; }
|
|
ShaderPerformanceMeasurer::Result& result (bool optimized) { return optimized ? m_optimizedResult : m_unoptimizedResult; }
|
|
|
|
State m_state;
|
|
ShaderPerformanceMeasurer m_measurer;
|
|
|
|
ProgramData m_unoptimizedData;
|
|
ProgramData m_optimizedData;
|
|
SharedPtr<const ShaderProgram> m_unoptimizedProgram;
|
|
SharedPtr<const ShaderProgram> m_optimizedProgram;
|
|
ShaderPerformanceMeasurer::Result m_unoptimizedResult;
|
|
ShaderPerformanceMeasurer::Result m_optimizedResult;
|
|
};
|
|
|
|
void ShaderOptimizationCase::init (void)
|
|
{
|
|
const glu::RenderContext& renderCtx = m_context.getRenderContext();
|
|
TestLog& log = m_testCtx.getLog();
|
|
|
|
m_measurer.logParameters(log);
|
|
|
|
for (int ndx = 0; ndx < 2; ndx++)
|
|
{
|
|
const bool optimized = ndx == 1;
|
|
|
|
programData(optimized) = generateProgramData(optimized);
|
|
|
|
for (int i = 0; i < (int)programData(optimized).attributes.size(); i++)
|
|
DE_ASSERT(programData(optimized).attributes[i].name != "a_position"); // \note Position attribute is set by m_measurer.
|
|
|
|
program(optimized) = SharedPtr<const ShaderProgram>(new ShaderProgram(renderCtx, programData(optimized).sources));
|
|
|
|
{
|
|
const tcu::ScopedLogSection section(log, optimized ? "OptimizedProgram" : "UnoptimizedProgram",
|
|
optimized ? "Hand-optimized program" : "Unoptimized program");
|
|
log << *program(optimized);
|
|
}
|
|
|
|
if (!program(optimized)->isOk())
|
|
TCU_FAIL("Shader compilation failed");
|
|
}
|
|
|
|
m_state = STATE_INIT_UNOPTIMIZED;
|
|
}
|
|
|
|
ShaderOptimizationCase::IterateResult ShaderOptimizationCase::iterate (void)
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
|
|
if (m_state == STATE_INIT_UNOPTIMIZED || m_state == STATE_INIT_OPTIMIZED)
|
|
{
|
|
const bool optimized = m_state == STATE_INIT_OPTIMIZED;
|
|
m_measurer.init(program(optimized)->getProgram(), programData(optimized).attributes, 1);
|
|
m_state = optimized ? STATE_MEASURE_OPTIMIZED : STATE_MEASURE_UNOPTIMIZED;
|
|
|
|
return CONTINUE;
|
|
}
|
|
else if (m_state == STATE_MEASURE_UNOPTIMIZED || m_state == STATE_MEASURE_OPTIMIZED)
|
|
{
|
|
m_measurer.iterate();
|
|
|
|
if (m_measurer.isFinished())
|
|
{
|
|
const bool optimized = m_state == STATE_MEASURE_OPTIMIZED;
|
|
const tcu::ScopedLogSection section (log, optimized ? "OptimizedResult" : "UnoptimizedResult",
|
|
optimized ? "Measurement results for hand-optimized program" : "Measurement result for unoptimized program");
|
|
m_measurer.logMeasurementInfo(log);
|
|
result(optimized) = m_measurer.getResult();
|
|
m_measurer.deinit();
|
|
m_state = optimized ? STATE_FINISHED : STATE_INIT_OPTIMIZED;
|
|
}
|
|
|
|
return CONTINUE;
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(m_state == STATE_FINISHED);
|
|
|
|
const float unoptimizedRelevantResult = m_caseShaderType == CASESHADERTYPE_VERTEX ? m_unoptimizedResult.megaVertPerSec : m_unoptimizedResult.megaFragPerSec;
|
|
const float optimizedRelevantResult = m_caseShaderType == CASESHADERTYPE_VERTEX ? m_optimizedResult.megaVertPerSec : m_optimizedResult.megaFragPerSec;
|
|
const char* const relevantResultName = m_caseShaderType == CASESHADERTYPE_VERTEX ? "vertex" : "fragment";
|
|
const float ratio = unoptimizedRelevantResult / optimizedRelevantResult;
|
|
const int handOptimizationGain = (int)deFloatRound(100.0f/ratio) - 100;
|
|
|
|
log << TestLog::Message << "Unoptimized / optimized " << relevantResultName << " performance ratio: " << ratio << TestLog::EndMessage;
|
|
|
|
if (handOptimizationGain >= 0)
|
|
log << TestLog::Message << "Note: " << handOptimizationGain << "% performance gain was achieved with hand-optimized version" << TestLog::EndMessage;
|
|
else
|
|
log << TestLog::Message << "Note: hand-optimization degraded performance by " << -handOptimizationGain << "%" << TestLog::EndMessage;
|
|
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString(ratio, 2).c_str());
|
|
|
|
return STOP;
|
|
}
|
|
}
|
|
|
|
class LoopUnrollCase : public ShaderOptimizationCase
|
|
{
|
|
public:
|
|
enum CaseType
|
|
{
|
|
CASETYPE_INDEPENDENT = 0,
|
|
CASETYPE_DEPENDENT,
|
|
|
|
CASETYPE_LAST
|
|
};
|
|
|
|
LoopUnrollCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType, CaseType caseType, int numRepetitions)
|
|
: ShaderOptimizationCase (context, name, description, caseShaderType)
|
|
, m_numRepetitions (numRepetitions)
|
|
, m_caseType (caseType)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
ProgramData generateProgramData (bool optimized) const
|
|
{
|
|
const string repetition = optimized ? repeatIndexedTemplate("\t" + expressionTemplate(m_caseType) + ";\n", m_numRepetitions)
|
|
: loop(m_numRepetitions, expressionTemplate(m_caseType));
|
|
|
|
return defaultProgramData(m_caseShaderType, "\t" + getShaderPrecision(m_caseShaderType) + " vec4 valueOrig = value;\n" + repetition);
|
|
}
|
|
|
|
private:
|
|
const int m_numRepetitions;
|
|
const CaseType m_caseType;
|
|
|
|
static inline string expressionTemplate (CaseType caseType)
|
|
{
|
|
switch (caseType)
|
|
{
|
|
case CASETYPE_INDEPENDENT: return "value += sin(float(${NDX}+1)*valueOrig)";
|
|
case CASETYPE_DEPENDENT: return "value = sin(value)";
|
|
default:
|
|
DE_ASSERT(false);
|
|
return DE_NULL;
|
|
}
|
|
}
|
|
|
|
static inline string loop (int iterations, const string& innerExpr)
|
|
{
|
|
return "\tfor (int i = 0; i < " + toString(iterations) + "; i++)\n\t\t" + tcu::StringTemplate(innerExpr).specialize(singleMap("NDX", "i")) + ";\n";
|
|
}
|
|
};
|
|
|
|
class LoopInvariantCodeMotionCase : public ShaderOptimizationCase
|
|
{
|
|
public:
|
|
LoopInvariantCodeMotionCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType, int numLoopIterations)
|
|
: ShaderOptimizationCase (context, name, description, caseShaderType)
|
|
, m_numLoopIterations (numLoopIterations)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
ProgramData generateProgramData (bool optimized) const
|
|
{
|
|
float scale = 0.0f;
|
|
for (int i = 0; i < m_numLoopIterations; i++)
|
|
scale += 3.2f*(float)i + 4.6f;
|
|
scale = 1.0f / scale;
|
|
|
|
const string precision = getShaderPrecision(m_caseShaderType);
|
|
const string statements = optimized ? " " + precision + " vec4 valueOrig = value;\n"
|
|
" " + precision + " vec4 y = sin(cos(sin(valueOrig)));\n"
|
|
" for (int i = 0; i < " + toString(m_numLoopIterations) + "; i++)\n"
|
|
" {\n"
|
|
" " + precision + " float x = 3.2*float(i) + 4.6;\n"
|
|
" value += x*y;\n"
|
|
" }\n"
|
|
" value *= " + toString(scale) + ";\n"
|
|
|
|
: " " + precision + " vec4 valueOrig = value;\n"
|
|
" for (int i = 0; i < " + toString(m_numLoopIterations) + "; i++)\n"
|
|
" {\n"
|
|
" " + precision + " float x = 3.2*float(i) + 4.6;\n"
|
|
" " + precision + " vec4 y = sin(cos(sin(valueOrig)));\n"
|
|
" value += x*y;\n"
|
|
" }\n"
|
|
" value *= " + toString(scale) + ";\n";
|
|
|
|
return defaultProgramData(m_caseShaderType, statements);
|
|
}
|
|
|
|
private:
|
|
const int m_numLoopIterations;
|
|
};
|
|
|
|
class FunctionInliningCase : public ShaderOptimizationCase
|
|
{
|
|
public:
|
|
FunctionInliningCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType, int callNestingDepth)
|
|
: ShaderOptimizationCase (context, name, description, caseShaderType)
|
|
, m_callNestingDepth (callNestingDepth)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
ProgramData generateProgramData (bool optimized) const
|
|
{
|
|
const string precision = getShaderPrecision(m_caseShaderType);
|
|
const string expression = "value*vec4(0.8, 0.7, 0.6, 0.9)";
|
|
const string maybeFuncDefs = optimized ? "" : funcDefinitions(m_callNestingDepth, precision, expression);
|
|
const string mainValueStatement = (optimized ? "\tvalue = " + expression : "\tvalue = func" + toString(m_callNestingDepth-1) + "(value)") + ";\n";
|
|
|
|
return defaultProgramData(m_caseShaderType, maybeFuncDefs, mainValueStatement);
|
|
}
|
|
|
|
private:
|
|
const int m_callNestingDepth;
|
|
|
|
static inline string funcDefinitions (int callNestingDepth, const string& precision, const string& expression)
|
|
{
|
|
string result = precision + " vec4 func0 (" + precision + " vec4 value) { return " + expression + "; }\n";
|
|
|
|
for (int i = 1; i < callNestingDepth; i++)
|
|
result += precision + " vec4 func" + toString(i) + " (" + precision + " vec4 v) { return func" + toString(i-1) + "(v); }\n";
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class ConstantPropagationCase : public ShaderOptimizationCase
|
|
{
|
|
public:
|
|
enum CaseType
|
|
{
|
|
CASETYPE_BUILT_IN_FUNCTIONS = 0,
|
|
CASETYPE_ARRAY,
|
|
CASETYPE_STRUCT,
|
|
|
|
CASETYPE_LAST
|
|
};
|
|
|
|
ConstantPropagationCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType, CaseType caseType, bool useConstantExpressionsOnly)
|
|
: ShaderOptimizationCase (context, name, description, caseShaderType)
|
|
, m_caseType (caseType)
|
|
, m_useConstantExpressionsOnly (useConstantExpressionsOnly)
|
|
{
|
|
DE_ASSERT(!(m_caseType == CASETYPE_ARRAY && m_useConstantExpressionsOnly)); // \note Would need array constructors, which GLSL ES 1 doesn't have.
|
|
}
|
|
|
|
protected:
|
|
ProgramData generateProgramData (bool optimized) const
|
|
{
|
|
const bool isVertexCase = m_caseShaderType == CASESHADERTYPE_VERTEX;
|
|
const string precision = getShaderPrecision(m_caseShaderType);
|
|
const string statements = m_caseType == CASETYPE_BUILT_IN_FUNCTIONS ? builtinFunctionsCaseStatements (optimized, m_useConstantExpressionsOnly, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_ARRAY ? arrayCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_STRUCT ? structCaseStatements (optimized, m_useConstantExpressionsOnly, precision, isVertexCase)
|
|
: DE_NULL;
|
|
|
|
return defaultProgramData(m_caseShaderType, statements);
|
|
}
|
|
|
|
private:
|
|
const CaseType m_caseType;
|
|
const bool m_useConstantExpressionsOnly;
|
|
|
|
static inline string builtinFunctionsCaseStatements (bool optimized, bool constantExpressionsOnly, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const string constMaybe = constantExpressionsOnly ? "const " : "";
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? " value = vec4(0.4, 0.5, 0.6, 0.7) * value; // NOTE: factor doesn't necessarily match the one in unoptimized shader, but shouldn't make a difference performance-wise\n"
|
|
|
|
: " " + constMaybe + precision + " vec4 a = vec4(sin(0.7), cos(0.2), sin(0.9), abs(-0.5));\n"
|
|
" " + constMaybe + precision + " vec4 b = cos(a) + fract(3.0*a.xzzw);\n"
|
|
" " + constMaybe + "bvec4 c = bvec4(true, false, true, true);\n"
|
|
" " + constMaybe + precision + " vec4 d = exp(b + vec4(c));\n"
|
|
" " + constMaybe + precision + " vec4 e0 = inversesqrt(mix(d+a, d+b, a));\n"
|
|
+ repeatIndexedTemplate(" " + constMaybe + precision + " vec4 e${NDX} = sin(sin(sin(sin(e${PREV_NDX}))));\n", numSinRows, "", 1) +
|
|
" " + constMaybe + precision + " vec4 f = abs(e" + toString(numSinRows) + ");\n" +
|
|
" value = f*value;\n";
|
|
}
|
|
|
|
static inline string arrayCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? " value = vec4(0.4, 0.5, 0.6, 0.7) * value; // NOTE: factor doesn't necessarily match the one in unoptimized shader, but shouldn't make a difference performance-wise\n"
|
|
|
|
: " const int arrLen = 4;\n"
|
|
" " + precision + " vec4 arr[arrLen];\n"
|
|
" arr[0] = vec4(0.1, 0.5, 0.9, 1.3);\n"
|
|
" arr[1] = vec4(0.2, 0.6, 1.0, 1.4);\n"
|
|
" arr[2] = vec4(0.3, 0.7, 1.1, 1.5);\n"
|
|
" arr[3] = vec4(0.4, 0.8, 1.2, 1.6);\n"
|
|
" " + precision + " vec4 a = (arr[0] + arr[1] + arr[2] + arr[3]) * 0.25;\n"
|
|
" " + precision + " vec4 b0 = cos(sin(a));\n"
|
|
+ repeatIndexedTemplate(" " + precision + " vec4 b${NDX} = sin(sin(sin(sin(b${PREV_NDX}))));\n", numSinRows, "", 1) +
|
|
" " + precision + " vec4 c = abs(b" + toString(numSinRows) + ");\n" +
|
|
" value = c*value;\n";
|
|
}
|
|
|
|
static inline string structCaseStatements (bool optimized, bool constantExpressionsOnly, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const string constMaybe = constantExpressionsOnly ? "const " : "";
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? " value = vec4(0.4, 0.5, 0.6, 0.7) * value; // NOTE: factor doesn't necessarily match the one in unoptimized shader, but shouldn't make a difference performance-wise\n"
|
|
|
|
: " struct S\n"
|
|
" {\n"
|
|
" " + precision + " vec4 a;\n"
|
|
" " + precision + " vec4 b;\n"
|
|
" " + precision + " vec4 c;\n"
|
|
" " + precision + " vec4 d;\n"
|
|
" };\n"
|
|
"\n"
|
|
" " + constMaybe + "S s =\n"
|
|
" S(vec4(0.1, 0.5, 0.9, 1.3),\n"
|
|
" vec4(0.2, 0.6, 1.0, 1.4),\n"
|
|
" vec4(0.3, 0.7, 1.1, 1.5),\n"
|
|
" vec4(0.4, 0.8, 1.2, 1.6));\n"
|
|
" " + constMaybe + precision + " vec4 a = (s.a + s.b + s.c + s.d) * 0.25;\n"
|
|
" " + constMaybe + precision + " vec4 b0 = cos(sin(a));\n"
|
|
+ repeatIndexedTemplate(" " + constMaybe + precision + " vec4 b${NDX} = sin(sin(sin(sin(b${PREV_NDX}))));\n", numSinRows, "", 1) +
|
|
" " + constMaybe + precision + " vec4 c = abs(b" + toString(numSinRows) + ");\n" +
|
|
" value = c*value;\n";
|
|
}
|
|
};
|
|
|
|
class CommonSubexpressionCase : public ShaderOptimizationCase
|
|
{
|
|
public:
|
|
enum CaseType
|
|
{
|
|
CASETYPE_SINGLE_STATEMENT = 0,
|
|
CASETYPE_MULTIPLE_STATEMENTS,
|
|
CASETYPE_STATIC_BRANCH,
|
|
CASETYPE_LOOP,
|
|
|
|
CASETYPE_LAST
|
|
};
|
|
|
|
CommonSubexpressionCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType, CaseType caseType)
|
|
: ShaderOptimizationCase (context, name, description, caseShaderType)
|
|
, m_caseType (caseType)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
ProgramData generateProgramData (bool optimized) const
|
|
{
|
|
const bool isVertexCase = m_caseShaderType == CASESHADERTYPE_VERTEX;
|
|
const string precision = getShaderPrecision(m_caseShaderType);
|
|
const string statements = m_caseType == CASETYPE_SINGLE_STATEMENT ? singleStatementCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_MULTIPLE_STATEMENTS ? multipleStatementsCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_STATIC_BRANCH ? staticBranchCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_LOOP ? loopCaseStatements (optimized, precision, isVertexCase)
|
|
: DE_NULL;
|
|
|
|
return defaultProgramData(m_caseShaderType, statements);
|
|
}
|
|
|
|
private:
|
|
const CaseType m_caseType;
|
|
|
|
static inline string singleStatementCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numTopLevelRepeats = useHeavierWorkload ? 4 : 1;
|
|
|
|
return optimized ? " " + precision + " vec4 s = sin(value);\n"
|
|
" " + precision + " vec4 cs = cos(s);\n"
|
|
" " + precision + " vec4 d = fract(s + cs) + sqrt(s + exp(cs));\n"
|
|
" value = " + repeat("d", numTopLevelRepeats, "+") + ";\n"
|
|
|
|
: " value = " + repeat("fract(sin(value) + cos(sin(value))) + sqrt(sin(value) + exp(cos(sin(value))))", numTopLevelRepeats, "\n\t + ") + ";\n";
|
|
}
|
|
|
|
static inline string multipleStatementsCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numTopLevelRepeats = useHeavierWorkload ? 4 : 2;
|
|
DE_ASSERT(numTopLevelRepeats >= 2);
|
|
|
|
return optimized ? " " + precision + " vec4 a = sin(value) + cos(exp(value));\n"
|
|
" " + precision + " vec4 b = cos(cos(a));\n"
|
|
" a = fract(exp(sqrt(b)));\n"
|
|
"\n"
|
|
+ repeat("\tvalue += a*b;\n", numTopLevelRepeats)
|
|
|
|
: repeatIndexedTemplate( " " + precision + " vec4 a${NDX} = sin(value) + cos(exp(value));\n"
|
|
" " + precision + " vec4 b${NDX} = cos(cos(a${NDX}));\n"
|
|
" a${NDX} = fract(exp(sqrt(b${NDX})));\n"
|
|
"\n",
|
|
numTopLevelRepeats) +
|
|
|
|
repeatIndexedTemplate( " value += a${NDX}*b${NDX};\n", numTopLevelRepeats);
|
|
}
|
|
|
|
static inline string staticBranchCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numTopLevelRepeats = useHeavierWorkload ? 4 : 2;
|
|
DE_ASSERT(numTopLevelRepeats >= 2);
|
|
|
|
if (optimized)
|
|
{
|
|
return " " + precision + " vec4 a = sin(value) + cos(exp(value));\n"
|
|
" " + precision + " vec4 b = cos(a);\n"
|
|
" b = cos(b);\n"
|
|
" a = fract(exp(sqrt(b)));\n"
|
|
"\n"
|
|
+ repeat(" value += a*b;\n", numTopLevelRepeats);
|
|
}
|
|
else
|
|
{
|
|
string result;
|
|
|
|
for (int i = 0; i < numTopLevelRepeats; i++)
|
|
{
|
|
result += " " + precision + " vec4 a" + toString(i) + " = sin(value) + cos(exp(value));\n"
|
|
" " + precision + " vec4 b" + toString(i) + " = cos(a" + toString(i) + ");\n";
|
|
|
|
if (i % 3 == 0)
|
|
result += " if (1 < 2)\n"
|
|
" b" + toString(i) + " = cos(b" + toString(i) + ");\n";
|
|
else if (i % 3 == 1)
|
|
result += " b" + toString(i) + " = cos(b" + toString(i) + ");\n";
|
|
else if (i % 3 == 2)
|
|
result += " if (2 < 1);\n"
|
|
" else\n"
|
|
" b" + toString(i) + " = cos(b" + toString(i) + ");\n";
|
|
else
|
|
DE_ASSERT(false);
|
|
|
|
result += " a" + toString(i) + " = fract(exp(sqrt(b" + toString(i) + ")));\n\n";
|
|
}
|
|
|
|
result += repeatIndexedTemplate(" value += a${NDX}*b${NDX};\n", numTopLevelRepeats);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static inline string loopCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numLoopIterations = useHeavierWorkload ? 32 : 4;
|
|
|
|
return optimized ? " " + precision + " vec4 acc = value;\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" acc = sin(acc);\n"
|
|
"\n"
|
|
" value += acc;\n"
|
|
" value += acc;\n"
|
|
|
|
: " " + precision + " vec4 acc0 = value;\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" acc0 = sin(acc0);\n"
|
|
"\n"
|
|
" " + precision + " vec4 acc1 = value;\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" acc1 = sin(acc1);\n"
|
|
"\n"
|
|
" value += acc0;\n"
|
|
" value += acc1;\n";
|
|
}
|
|
};
|
|
|
|
class DeadCodeEliminationCase : public ShaderOptimizationCase
|
|
{
|
|
public:
|
|
enum CaseType
|
|
{
|
|
CASETYPE_DEAD_BRANCH_SIMPLE = 0,
|
|
CASETYPE_DEAD_BRANCH_COMPLEX,
|
|
CASETYPE_DEAD_BRANCH_COMPLEX_NO_CONST,
|
|
CASETYPE_DEAD_BRANCH_FUNC_CALL,
|
|
CASETYPE_UNUSED_VALUE_BASIC,
|
|
CASETYPE_UNUSED_VALUE_LOOP,
|
|
CASETYPE_UNUSED_VALUE_DEAD_BRANCH,
|
|
CASETYPE_UNUSED_VALUE_AFTER_RETURN,
|
|
CASETYPE_UNUSED_VALUE_MUL_ZERO,
|
|
|
|
CASETYPE_LAST
|
|
};
|
|
|
|
DeadCodeEliminationCase (Context& context, const char* name, const char* description, CaseShaderType caseShaderType, CaseType caseType)
|
|
: ShaderOptimizationCase (context, name, description, caseShaderType)
|
|
, m_caseType (caseType)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
ProgramData generateProgramData (bool optimized) const
|
|
{
|
|
const bool isVertexCase = m_caseShaderType == CASESHADERTYPE_VERTEX;
|
|
const string precision = getShaderPrecision(m_caseShaderType);
|
|
const string funcDefs = m_caseType == CASETYPE_DEAD_BRANCH_FUNC_CALL ? deadBranchFuncCallCaseFuncDefs (optimized, precision)
|
|
: m_caseType == CASETYPE_UNUSED_VALUE_AFTER_RETURN ? unusedValueAfterReturnCaseFuncDefs (optimized, precision, isVertexCase)
|
|
: "";
|
|
|
|
const string statements = m_caseType == CASETYPE_DEAD_BRANCH_SIMPLE ? deadBranchSimpleCaseStatements (optimized, isVertexCase)
|
|
: m_caseType == CASETYPE_DEAD_BRANCH_COMPLEX ? deadBranchComplexCaseStatements (optimized, precision, true, isVertexCase)
|
|
: m_caseType == CASETYPE_DEAD_BRANCH_COMPLEX_NO_CONST ? deadBranchComplexCaseStatements (optimized, precision, false, isVertexCase)
|
|
: m_caseType == CASETYPE_DEAD_BRANCH_FUNC_CALL ? deadBranchFuncCallCaseStatements (optimized, isVertexCase)
|
|
: m_caseType == CASETYPE_UNUSED_VALUE_BASIC ? unusedValueBasicCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_UNUSED_VALUE_LOOP ? unusedValueLoopCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_UNUSED_VALUE_DEAD_BRANCH ? unusedValueDeadBranchCaseStatements (optimized, precision, isVertexCase)
|
|
: m_caseType == CASETYPE_UNUSED_VALUE_AFTER_RETURN ? unusedValueAfterReturnCaseStatements ()
|
|
: m_caseType == CASETYPE_UNUSED_VALUE_MUL_ZERO ? unusedValueMulZeroCaseStatements (optimized, precision, isVertexCase)
|
|
: DE_NULL;
|
|
|
|
return defaultProgramData(m_caseShaderType, funcDefs, statements);
|
|
}
|
|
|
|
private:
|
|
const CaseType m_caseType;
|
|
|
|
static inline string deadBranchSimpleCaseStatements (bool optimized, bool useHeavierWorkload)
|
|
{
|
|
const int numLoopIterations = useHeavierWorkload ? 16 : 4;
|
|
|
|
return optimized ? " value = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
|
|
: " value = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" if (2 < 1)\n"
|
|
" {\n"
|
|
" value = cos(exp(sin(value))*log(sqrt(value)));\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" value = sin(value);\n"
|
|
" }\n";
|
|
}
|
|
|
|
static inline string deadBranchComplexCaseStatements (bool optimized, const string& precision, bool useConst, bool useHeavierWorkload)
|
|
{
|
|
const string constMaybe = useConst ? "const " : "";
|
|
const int numLoopIterations = useHeavierWorkload ? 16 : 4;
|
|
|
|
return optimized ? " value = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
|
|
: " value = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" " + constMaybe + precision + " vec4 a = vec4(sin(0.7), cos(0.2), sin(0.9), abs(-0.5));\n"
|
|
" " + constMaybe + precision + " vec4 b = cos(a) + fract(3.0*a.xzzw);\n"
|
|
" " + constMaybe + "bvec4 c = bvec4(true, false, true, true);\n"
|
|
" " + constMaybe + precision + " vec4 d = exp(b + vec4(c));\n"
|
|
" " + constMaybe + precision + " vec4 e = 1.8*abs(sin(sin(inversesqrt(mix(d+a, d+b, a)))));\n"
|
|
" if (e.x > 1.0)\n"
|
|
" {\n"
|
|
" value = cos(exp(sin(value))*log(sqrt(value)));\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" value = sin(value);\n"
|
|
" }\n";
|
|
}
|
|
|
|
static inline string deadBranchFuncCallCaseFuncDefs (bool optimized, const string& precision)
|
|
{
|
|
return optimized ? "" : precision + " float func (" + precision + " float x) { return 2.0*x; }\n";
|
|
}
|
|
|
|
static inline string deadBranchFuncCallCaseStatements (bool optimized, bool useHeavierWorkload)
|
|
{
|
|
const int numLoopIterations = useHeavierWorkload ? 16 : 4;
|
|
|
|
return optimized ? " value = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
|
|
: " value = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" if (func(0.3) > 1.0)\n"
|
|
" {\n"
|
|
" value = cos(exp(sin(value))*log(sqrt(value)));\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" value = sin(value);\n"
|
|
" }\n";
|
|
}
|
|
|
|
static inline string unusedValueBasicCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" value = used;\n"
|
|
|
|
: " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" " + precision + " vec4 unused = cos(exp(sin(value))*log(sqrt(value))) + used;\n"
|
|
+ repeat(" unused = sin(sin(sin(sin(unused))));\n", numSinRows) +
|
|
" value = used;\n";
|
|
}
|
|
|
|
static inline string unusedValueLoopCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numLoopIterations = useHeavierWorkload ? 16 : 4;
|
|
|
|
return optimized ? " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" value = used;\n"
|
|
|
|
: " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" " + precision + " vec4 unused = cos(exp(sin(value))*log(sqrt(value)));\n"
|
|
" for (int i = 0; i < " + toString(numLoopIterations) + "; i++)\n"
|
|
" unused = sin(unused + used);\n"
|
|
" value = used;\n";
|
|
}
|
|
|
|
static inline string unusedValueAfterReturnCaseFuncDefs (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? precision + " vec4 func (" + precision + " vec4 v)\n"
|
|
"{\n"
|
|
" " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * v;\n"
|
|
" return used;\n"
|
|
"}\n"
|
|
|
|
: precision + " vec4 func (" + precision + " vec4 v)\n"
|
|
"{\n"
|
|
" " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * v;\n"
|
|
" " + precision + " vec4 unused = cos(exp(sin(v))*log(sqrt(v)));\n"
|
|
+ repeat(" unused = sin(sin(sin(sin(unused))));\n", numSinRows) +
|
|
" return used;\n"
|
|
" used = used*unused;"
|
|
" return used;\n"
|
|
"}\n";
|
|
}
|
|
|
|
static inline string unusedValueAfterReturnCaseStatements (void)
|
|
{
|
|
return " value = func(value);\n";
|
|
}
|
|
|
|
static inline string unusedValueDeadBranchCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" value = used;\n"
|
|
|
|
: " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" " + precision + " vec4 unused = cos(exp(sin(value))*log(sqrt(value)));\n"
|
|
+ repeat(" unused = sin(sin(sin(sin(unused))));\n", numSinRows) +
|
|
" if (2 < 1)\n"
|
|
" used = used*unused;\n"
|
|
" value = used;\n";
|
|
}
|
|
|
|
static inline string unusedValueMulZeroCaseStatements (bool optimized, const string& precision, bool useHeavierWorkload)
|
|
{
|
|
const int numSinRows = useHeavierWorkload ? 12 : 1;
|
|
|
|
return optimized ? " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" value = used;\n"
|
|
|
|
: " " + precision + " vec4 used = vec4(0.6, 0.7, 0.8, 0.9) * value;\n"
|
|
" " + precision + " vec4 unused = cos(exp(sin(value))*log(sqrt(value)));\n"
|
|
+ repeat(" unused = sin(sin(sin(sin(unused))));\n", numSinRows) +
|
|
" value = used + unused*float(1-1);\n";
|
|
}
|
|
};
|
|
|
|
} // anonymous
|
|
|
|
ShaderOptimizationTests::ShaderOptimizationTests (Context& context)
|
|
: TestCaseGroup(context, "optimization", "Shader Optimization Performance Tests")
|
|
{
|
|
}
|
|
|
|
ShaderOptimizationTests::~ShaderOptimizationTests (void)
|
|
{
|
|
}
|
|
|
|
void ShaderOptimizationTests::init (void)
|
|
{
|
|
TestCaseGroup* const unrollGroup = new TestCaseGroup(m_context, "loop_unrolling", "Loop Unrolling Cases");
|
|
TestCaseGroup* const loopInvariantCodeMotionGroup = new TestCaseGroup(m_context, "loop_invariant_code_motion", "Loop-Invariant Code Motion Cases");
|
|
TestCaseGroup* const inlineGroup = new TestCaseGroup(m_context, "function_inlining", "Function Inlining Cases");
|
|
TestCaseGroup* const constantPropagationGroup = new TestCaseGroup(m_context, "constant_propagation", "Constant Propagation Cases");
|
|
TestCaseGroup* const commonSubexpressionGroup = new TestCaseGroup(m_context, "common_subexpression_elimination", "Common Subexpression Elimination Cases");
|
|
TestCaseGroup* const deadCodeEliminationGroup = new TestCaseGroup(m_context, "dead_code_elimination", "Dead Code Elimination Cases");
|
|
addChild(unrollGroup);
|
|
addChild(loopInvariantCodeMotionGroup);
|
|
addChild(inlineGroup);
|
|
addChild(constantPropagationGroup);
|
|
addChild(commonSubexpressionGroup);
|
|
addChild(deadCodeEliminationGroup);
|
|
|
|
for (int caseShaderTypeI = 0; caseShaderTypeI < CASESHADERTYPE_LAST; caseShaderTypeI++)
|
|
{
|
|
const CaseShaderType caseShaderType = (CaseShaderType)caseShaderTypeI;
|
|
const char* const caseShaderTypeSuffix = caseShaderType == CASESHADERTYPE_VERTEX ? "_vertex"
|
|
: caseShaderType == CASESHADERTYPE_FRAGMENT ? "_fragment"
|
|
: DE_NULL;
|
|
|
|
// Loop unrolling cases.
|
|
|
|
{
|
|
static const int loopIterationCounts[] = { 4, 8, 32 };
|
|
|
|
for (int caseTypeI = 0; caseTypeI < LoopUnrollCase::CASETYPE_LAST; caseTypeI++)
|
|
{
|
|
const LoopUnrollCase::CaseType caseType = (LoopUnrollCase::CaseType)caseTypeI;
|
|
const string caseTypeName = caseType == LoopUnrollCase::CASETYPE_INDEPENDENT ? "independent_iterations"
|
|
: caseType == LoopUnrollCase::CASETYPE_DEPENDENT ? "dependent_iterations"
|
|
: DE_NULL;
|
|
const string caseTypeDesc = caseType == LoopUnrollCase::CASETYPE_INDEPENDENT ? "loop iterations don't depend on each other"
|
|
: caseType == LoopUnrollCase::CASETYPE_DEPENDENT ? "loop iterations depend on each other"
|
|
: DE_NULL;
|
|
|
|
for (int loopIterNdx = 0; loopIterNdx < DE_LENGTH_OF_ARRAY(loopIterationCounts); loopIterNdx++)
|
|
{
|
|
const int loopIterations = loopIterationCounts[loopIterNdx];
|
|
const string name = caseTypeName + "_" + toString(loopIterations) + caseShaderTypeSuffix;
|
|
const string description = toString(loopIterations) + " iterations; " + caseTypeDesc;
|
|
|
|
unrollGroup->addChild(new LoopUnrollCase(m_context, name.c_str(), description.c_str(), caseShaderType, caseType, loopIterations));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Loop-invariant code motion cases.
|
|
|
|
{
|
|
static const int loopIterationCounts[] = { 4, 8, 32 };
|
|
|
|
for (int loopIterNdx = 0; loopIterNdx < DE_LENGTH_OF_ARRAY(loopIterationCounts); loopIterNdx++)
|
|
{
|
|
const int loopIterations = loopIterationCounts[loopIterNdx];
|
|
const string name = toString(loopIterations) + "_iterations" + caseShaderTypeSuffix;
|
|
|
|
loopInvariantCodeMotionGroup->addChild(new LoopInvariantCodeMotionCase(m_context, name.c_str(), "", caseShaderType, loopIterations));
|
|
}
|
|
}
|
|
|
|
// Function inlining cases.
|
|
|
|
{
|
|
static const int callNestingDepths[] = { 4, 8, 32 };
|
|
|
|
for (int nestDepthNdx = 0; nestDepthNdx < DE_LENGTH_OF_ARRAY(callNestingDepths); nestDepthNdx++)
|
|
{
|
|
const int nestingDepth = callNestingDepths[nestDepthNdx];
|
|
const string name = toString(nestingDepth) + "_nested" + caseShaderTypeSuffix;
|
|
|
|
inlineGroup->addChild(new FunctionInliningCase(m_context, name.c_str(), "", caseShaderType, nestingDepth));
|
|
}
|
|
}
|
|
|
|
// Constant propagation cases.
|
|
|
|
for (int caseTypeI = 0; caseTypeI < ConstantPropagationCase::CASETYPE_LAST; caseTypeI++)
|
|
{
|
|
const ConstantPropagationCase::CaseType caseType = (ConstantPropagationCase::CaseType)caseTypeI;
|
|
const string caseTypeName = caseType == ConstantPropagationCase::CASETYPE_BUILT_IN_FUNCTIONS ? "built_in_functions"
|
|
: caseType == ConstantPropagationCase::CASETYPE_ARRAY ? "array"
|
|
: caseType == ConstantPropagationCase::CASETYPE_STRUCT ? "struct"
|
|
: DE_NULL;
|
|
|
|
for (int constantExpressionsOnlyI = 0; constantExpressionsOnlyI <= 1; constantExpressionsOnlyI++)
|
|
{
|
|
const bool constantExpressionsOnly = constantExpressionsOnlyI != 0;
|
|
const string name = caseTypeName + (constantExpressionsOnly ? "" : "_no_const") + caseShaderTypeSuffix;
|
|
|
|
if (caseType == ConstantPropagationCase::CASETYPE_ARRAY && constantExpressionsOnly) // \note See ConstantPropagationCase's constructor for explanation.
|
|
continue;
|
|
|
|
constantPropagationGroup->addChild(new ConstantPropagationCase(m_context, name.c_str(), "", caseShaderType, caseType, constantExpressionsOnly));
|
|
}
|
|
}
|
|
|
|
// Common subexpression cases.
|
|
|
|
for (int caseTypeI = 0; caseTypeI < CommonSubexpressionCase::CASETYPE_LAST; caseTypeI++)
|
|
{
|
|
const CommonSubexpressionCase::CaseType caseType = (CommonSubexpressionCase::CaseType)caseTypeI;
|
|
|
|
const string caseTypeName = caseType == CommonSubexpressionCase::CASETYPE_SINGLE_STATEMENT ? "single_statement"
|
|
: caseType == CommonSubexpressionCase::CASETYPE_MULTIPLE_STATEMENTS ? "multiple_statements"
|
|
: caseType == CommonSubexpressionCase::CASETYPE_STATIC_BRANCH ? "static_branch"
|
|
: caseType == CommonSubexpressionCase::CASETYPE_LOOP ? "loop"
|
|
: DE_NULL;
|
|
|
|
const string description = caseType == CommonSubexpressionCase::CASETYPE_SINGLE_STATEMENT ? "A single statement containing multiple uses of same subexpression"
|
|
: caseType == CommonSubexpressionCase::CASETYPE_MULTIPLE_STATEMENTS ? "Multiple statements performing same computations"
|
|
: caseType == CommonSubexpressionCase::CASETYPE_STATIC_BRANCH ? "Multiple statements including a static conditional"
|
|
: caseType == CommonSubexpressionCase::CASETYPE_LOOP ? "Multiple loops performing the same computations"
|
|
: DE_NULL;
|
|
|
|
commonSubexpressionGroup->addChild(new CommonSubexpressionCase(m_context, (caseTypeName + caseShaderTypeSuffix).c_str(), description.c_str(), caseShaderType, caseType));
|
|
}
|
|
|
|
// Dead code elimination cases.
|
|
|
|
for (int caseTypeI = 0; caseTypeI < DeadCodeEliminationCase::CASETYPE_LAST; caseTypeI++)
|
|
{
|
|
const DeadCodeEliminationCase::CaseType caseType = (DeadCodeEliminationCase::CaseType)caseTypeI;
|
|
const char* const caseTypeName = caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_SIMPLE ? "dead_branch_simple"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_COMPLEX ? "dead_branch_complex"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_COMPLEX_NO_CONST ? "dead_branch_complex_no_const"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_FUNC_CALL ? "dead_branch_func_call"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_BASIC ? "unused_value_basic"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_LOOP ? "unused_value_loop"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_DEAD_BRANCH ? "unused_value_dead_branch"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_AFTER_RETURN ? "unused_value_after_return"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_MUL_ZERO ? "unused_value_mul_zero"
|
|
: DE_NULL;
|
|
|
|
const char* const caseTypeDescription = caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_SIMPLE ? "Do computation inside a branch that is never taken (condition is simple false constant expression)"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_COMPLEX ? "Do computation inside a branch that is never taken (condition is complex false constant expression)"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_COMPLEX_NO_CONST ? "Do computation inside a branch that is never taken (condition is complex false expression, not constant expression but still compile-time computable)"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_DEAD_BRANCH_FUNC_CALL ? "Do computation inside a branch that is never taken (condition is compile-time computable false expression containing function call to a simple inlineable function)"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_BASIC ? "Compute a value that is never used even statically"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_LOOP ? "Compute a value, using a loop, that is never used even statically"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_DEAD_BRANCH ? "Compute a value that is used only inside a statically dead branch"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_AFTER_RETURN ? "Compute a value that is used only after a return statement"
|
|
: caseType == DeadCodeEliminationCase::CASETYPE_UNUSED_VALUE_MUL_ZERO ? "Compute a value that is used but multiplied by a zero constant expression"
|
|
: DE_NULL;
|
|
|
|
deadCodeEliminationGroup->addChild(new DeadCodeEliminationCase(m_context, (string() + caseTypeName + caseShaderTypeSuffix).c_str(), caseTypeDescription, caseShaderType, caseType));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // Performance
|
|
} // gles2
|
|
} // deqp
|