blob: 3f38c25e593aaf2ee18148ff2696e6b1b6cb7db9 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program Random Shader Generator
* ----------------------------------------------------
*
* 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 generator.
*//*--------------------------------------------------------------------*/
#include "rsgShaderGenerator.hpp"
#include "rsgFunctionGenerator.hpp"
#include "rsgToken.hpp"
#include "rsgPrettyPrinter.hpp"
#include "rsgUtils.hpp"
#include "deString.h"
#include <iterator>
using std::string;
using std::vector;
namespace rsg
{
ShaderGenerator::ShaderGenerator(GeneratorState &state) : m_state(state), m_varManager(state.getNameAllocator())
{
state.setVariableManager(m_varManager);
}
ShaderGenerator::~ShaderGenerator(void)
{
}
namespace
{
const char *getFragColorName(const GeneratorState &state)
{
switch (state.getProgramParameters().version)
{
case VERSION_100:
return "gl_FragColor";
case VERSION_300:
return "dEQP_FragColor";
default:
DE_ASSERT(false);
return DE_NULL;
}
}
void createAssignment(BlockStatement &block, const Variable *dstVar, const Variable *srcVar)
{
VariableRead *varRead = new VariableRead(srcVar);
try
{
block.addChild(new AssignStatement(dstVar, varRead));
}
catch (const std::exception &)
{
delete varRead;
throw;
}
}
const ValueEntry *findByName(VariableManager &varManager, const char *name)
{
AnyEntry::Iterator iter = varManager.getBegin<AnyEntry>();
AnyEntry::Iterator end = varManager.getEnd<AnyEntry>();
for (; iter != end; iter++)
{
const ValueEntry *entry = *iter;
if (deStringEqual(entry->getVariable()->getName(), name))
return entry;
}
return DE_NULL;
}
void genVertexPassthrough(GeneratorState &state, Shader &shader)
{
// Create copies from shader inputs to outputs
vector<const ValueEntry *> entries;
std::copy(state.getVariableManager().getBegin<AnyEntry>(), state.getVariableManager().getEnd<AnyEntry>(),
std::inserter(entries, entries.begin()));
for (vector<const ValueEntry *>::const_iterator i = entries.begin(); i != entries.end(); i++)
{
const ValueEntry *entry = *i;
const Variable *outVar = entry->getVariable();
std::string inVarName;
if (outVar->getStorage() != Variable::STORAGE_SHADER_OUT)
continue;
// Name: a_[name], remove v_ -prefix if such exists
inVarName = "a_";
if (deStringBeginsWith(outVar->getName(), "v_"))
inVarName += (outVar->getName() + 2);
else
inVarName += outVar->getName();
Variable *inVar =
state.getVariableManager().allocate(outVar->getType(), Variable::STORAGE_SHADER_IN, inVarName.c_str());
// Update value range. This will be stored into shader input info.
state.getVariableManager().setValue(inVar, entry->getValueRange());
// Add assignment from input to output into main() body
createAssignment(shader.getMain().getBody(), entry->getVariable(), inVar);
}
}
void genFragmentPassthrough(GeneratorState &state, Shader &shader)
{
// Add simple gl_FragColor = v_color; assignment
const ValueEntry *fragColorEntry = findByName(state.getVariableManager(), getFragColorName(state));
TCU_CHECK(fragColorEntry);
Variable *inColorVariable = state.getVariableManager().allocate(fragColorEntry->getVariable()->getType(),
Variable::STORAGE_SHADER_IN, "v_color");
state.getVariableManager().setValue(inColorVariable, fragColorEntry->getValueRange());
createAssignment(shader.getMain().getBody(), fragColorEntry->getVariable(), inColorVariable);
}
// Sets undefined (-inf..inf) components to some meaningful values. Used for sanitizing final shader input value ranges.
void fillUndefinedComponents(ValueRangeAccess valueRange)
{
VariableType::Type baseType = valueRange.getType().getBaseType();
TCU_CHECK(baseType == VariableType::TYPE_FLOAT || baseType == VariableType::TYPE_INT ||
baseType == VariableType::TYPE_BOOL);
for (int elemNdx = 0; elemNdx < valueRange.getType().getNumElements(); elemNdx++)
{
if (isUndefinedValueRange(valueRange.component(elemNdx)))
{
ValueAccess min = valueRange.component(elemNdx).getMin();
ValueAccess max = valueRange.component(elemNdx).getMax();
switch (baseType)
{
case VariableType::TYPE_FLOAT:
min = 0.0f;
max = 1.0f;
break;
case VariableType::TYPE_INT:
min = 0;
max = 1;
break;
case VariableType::TYPE_BOOL:
min = false;
max = true;
break;
default:
DE_ASSERT(false);
}
}
}
}
void fillUndefinedShaderInputs(vector<ShaderInput *> &inputs)
{
for (vector<ShaderInput *>::iterator i = inputs.begin(); i != inputs.end(); i++)
{
if (!(*i)->getVariable()->getType().isSampler()) // Samplers are assigned at program-level.
fillUndefinedComponents((*i)->getValueRange());
}
}
} // namespace
void ShaderGenerator::generate(const ShaderParameters &shaderParams, Shader &shader,
const vector<ShaderInput *> &outputs)
{
// Global scopes
VariableScope &globalVariableScope = shader.getGlobalScope();
ValueScope globalValueScope;
// Init state
m_state.setShader(shaderParams, shader);
DE_ASSERT(m_state.getExpressionFlags() == 0);
// Reserve some scalars for gl_Position & dEQP_Position
ReservedScalars reservedScalars;
if (shader.getType() == Shader::TYPE_VERTEX)
m_state.getVariableManager().reserve(reservedScalars, 4 * 2);
// Push global scopes
m_varManager.pushVariableScope(globalVariableScope);
m_varManager.pushValueScope(globalValueScope);
// Init shader outputs.
{
for (vector<ShaderInput *>::const_iterator i = outputs.begin(); i != outputs.end(); i++)
{
const ShaderInput *input = *i;
Variable *variable = m_state.getVariableManager().allocate(
input->getVariable()->getType(), Variable::STORAGE_SHADER_OUT, input->getVariable()->getName());
m_state.getVariableManager().setValue(variable, input->getValueRange());
}
if (shader.getType() == Shader::TYPE_FRAGMENT)
{
// gl_FragColor
// \todo [2011-11-22 pyry] Multiple outputs from fragment shader!
Variable *fragColorVar = m_state.getVariableManager().allocate(
VariableType(VariableType::TYPE_FLOAT, 4), Variable::STORAGE_SHADER_OUT, getFragColorName(m_state));
ValueRange valueRange(fragColorVar->getType());
valueRange.getMin() = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
valueRange.getMax() = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
fragColorVar->setLayoutLocation(0); // Bind color output to location 0 (applies to GLSL ES 3.0 onwards).
m_state.getVariableManager().setValue(fragColorVar, valueRange.asAccess());
}
}
// Construct shader code.
{
Function &main = shader.getMain();
main.setReturnType(VariableType(VariableType::TYPE_VOID));
if (shaderParams.randomize)
{
FunctionGenerator funcGen(m_state, main);
// Mandate assignment into to all shader outputs in main()
const vector<Variable *> &liveVars = globalVariableScope.getLiveVariables();
for (vector<Variable *>::const_iterator i = liveVars.begin(); i != liveVars.end(); i++)
{
Variable *variable = *i;
if (variable->getStorage() == Variable::STORAGE_SHADER_OUT)
funcGen.requireAssignment(variable);
}
funcGen.generate();
}
else
{
if (shader.getType() == Shader::TYPE_VERTEX)
genVertexPassthrough(m_state, shader);
else
{
DE_ASSERT(shader.getType() == Shader::TYPE_FRAGMENT);
genFragmentPassthrough(m_state, shader);
}
}
if (shader.getType() == Shader::TYPE_VERTEX)
{
// Add gl_Position = dEQP_Position;
m_state.getVariableManager().release(reservedScalars);
Variable *glPosVariable = m_state.getVariableManager().allocate(
VariableType(VariableType::TYPE_FLOAT, 4), Variable::STORAGE_SHADER_OUT, "gl_Position");
Variable *qpPosVariable = m_state.getVariableManager().allocate(
VariableType(VariableType::TYPE_FLOAT, 4), Variable::STORAGE_SHADER_IN, "dEQP_Position");
ValueRange valueRange(glPosVariable->getType());
valueRange.getMin() = tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f);
valueRange.getMax() = tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f);
m_state.getVariableManager().setValue(
qpPosVariable,
valueRange
.asAccess()); // \todo [2011-05-24 pyry] No expression should be able to use gl_Position or dEQP_Position..
createAssignment(main.getBody(), glPosVariable, qpPosVariable);
}
}
// Declare live global variables.
{
vector<Variable *> liveVariables;
std::copy(globalVariableScope.getLiveVariables().begin(), globalVariableScope.getLiveVariables().end(),
std::inserter(liveVariables, liveVariables.begin()));
vector<Variable *> createDeclarationStatementVars;
for (vector<Variable *>::iterator i = liveVariables.begin(); i != liveVariables.end(); i++)
{
Variable *variable = *i;
const char *name = variable->getName();
bool declare = !deStringBeginsWith(name, "gl_"); // Do not declare built-in types.
// Create input entries (store value range) if necessary
vector<ShaderInput *> &inputs = shader.getInputs();
vector<ShaderInput *> &uniforms = shader.getUniforms();
switch (variable->getStorage())
{
case Variable::STORAGE_SHADER_IN:
{
const ValueEntry *value = m_state.getVariableManager().getValue(variable);
inputs.reserve(inputs.size() + 1);
inputs.push_back(new ShaderInput(variable, value->getValueRange()));
break;
}
case Variable::STORAGE_UNIFORM:
{
const ValueEntry *value = m_state.getVariableManager().getValue(variable);
uniforms.reserve(uniforms.size() + 1);
uniforms.push_back(new ShaderInput(variable, value->getValueRange()));
break;
}
default:
break;
}
if (declare)
createDeclarationStatementVars.push_back(variable);
else
{
// Just move to global scope without declaration statement.
m_state.getVariableManager().declareVariable(variable);
}
}
// All global initializers must be constant expressions, no variable allocation is allowed
DE_ASSERT(m_state.getExpressionFlags() == 0);
m_state.pushExpressionFlags(CONST_EXPR | NO_VAR_ALLOCATION);
// Create declaration statements
for (vector<Variable *>::iterator i = createDeclarationStatementVars.begin();
i != createDeclarationStatementVars.end(); i++)
{
shader.getGlobalStatements().reserve(shader.getGlobalStatements().size());
shader.getGlobalStatements().push_back(new DeclarationStatement(m_state, *i));
}
m_state.popExpressionFlags();
}
// Pop global scopes
m_varManager.popVariableScope();
m_varManager.popValueScope();
// Fill undefined (unused) components in inputs with unused values
fillUndefinedShaderInputs(shader.getInputs());
fillUndefinedShaderInputs(shader.getUniforms());
// Tokenize shader and write source
{
TokenStream tokenStr;
shader.tokenize(m_state, tokenStr);
std::ostringstream str;
PrettyPrinter printer(str);
// Append #version if necessary.
if (m_state.getProgramParameters().version == VERSION_300)
str << "#version 300 es\n";
printer.append(tokenStr);
shader.setSource(str.str().c_str());
}
}
} // namespace rsg