/*-------------------------------------------------------------------------
* 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 Statements.
*//*--------------------------------------------------------------------*/
#include "rsgStatement.hpp"
#include "rsgExpressionGenerator.hpp"
#include "rsgUtils.hpp"
#include <typeinfo>
using std::vector;
namespace rsg
{
namespace
{
inline bool isCurrentTopStatementBlock (const GeneratorState& state)
{
int stackDepth = state.getStatementDepth();
return dynamic_cast<const BlockStatement*>(state.getStatementStackEntry(stackDepth-1)) != DE_NULL;
}
template <class T> float getWeight (const GeneratorState& state) { return T::getWeight(state); }
template <class T> Statement* create (GeneratorState& state) { return new T(state); }
struct StatementSpec
{
float (*getWeight) (const GeneratorState& state);
Statement* (*create) (GeneratorState& state);
};
const StatementSpec* chooseStatement (GeneratorState& state)
{
static const StatementSpec statementSpecs[] =
{
{ getWeight<BlockStatement>, create<BlockStatement> },
{ getWeight<ExpressionStatement>, create<ExpressionStatement> },
{ getWeight<DeclarationStatement>, create<DeclarationStatement> },
{ getWeight<ConditionalStatement>, create<ConditionalStatement> }
};
float weights[DE_LENGTH_OF_ARRAY(statementSpecs)];
// Compute weights
float sum = 0.0f;
for (int ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(statementSpecs); ndx++)
{
weights[ndx] = statementSpecs[ndx].getWeight(state);
sum += weights[ndx];
}
DE_ASSERT(sum > 0.0f);
// Random number in range
float p = state.getRandom().getFloat(0.0f, sum);
const StatementSpec* spec = DE_NULL;
const StatementSpec* lastNonZero = DE_NULL;
// Find element in that point
sum = 0.0f;
for (int ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(statementSpecs); ndx++)
{
sum += weights[ndx];
if (p < sum)
{
spec = &statementSpecs[ndx];
break;
}
else if (weights[ndx] > 0.0f)
lastNonZero = &statementSpecs[ndx];
}
if (!spec)
spec = lastNonZero;
return spec;
}
Statement* createStatement (GeneratorState& state)
{
return chooseStatement(state)->create(state);
}
} // anonymous
Statement::Statement (void)
{
}
Statement::~Statement (void)
{
}
ExpressionStatement::ExpressionStatement (GeneratorState& state)
: m_expression(DE_NULL)
{
ExpressionGenerator generator(state);
m_expression = generator.generate(ValueRange(VariableType(VariableType::TYPE_VOID)));
}
ExpressionStatement::~ExpressionStatement (void)
{
delete m_expression;
}
float ExpressionStatement::getWeight (const GeneratorState& state)
{
DE_UNREF(state);
return 1.0f;
}
void ExpressionStatement::execute (ExecutionContext& execCtx) const
{
m_expression->evaluate(execCtx);
}
BlockStatement::BlockStatement (GeneratorState& state)
{
init(state);
}
void BlockStatement::init (GeneratorState& state)
{
// Select number of children statements to construct
m_numChildrenToCreate = state.getRandom().getInt(0, state.getShaderParameters().maxStatementsPerBlock);
// Push scope
state.getVariableManager().pushVariableScope(m_scope);
}
BlockStatement::~BlockStatement (void)
{
for (vector<Statement*>::iterator i = m_children.begin(); i != m_children.end(); i++)
delete *i;
m_children.clear();
}
void BlockStatement::addChild (Statement* statement)
{
try
{
m_children.push_back(statement);
}
catch (const std::exception&)
{
delete statement;
throw;
}
}
Statement* BlockStatement::createNextChild (GeneratorState& state)
{
if ((int)m_children.size() < m_numChildrenToCreate)
{
// Select and create a new child
Statement* child = createStatement(state);
addChild(child);
return child;
}
else
{
// Done, pop scope
state.getVariableManager().popVariableScope();
return DE_NULL;
}
}
float BlockStatement::getWeight (const GeneratorState& state)
{
if (state.getStatementDepth()+1 < state.getShaderParameters().maxStatementDepth)
{
if (isCurrentTopStatementBlock(state))
return 0.2f; // Low probability for anonymous blocks.
else
return 1.0f;
}
else
return 0.0f;
}
void BlockStatement::tokenize (GeneratorState& state, TokenStream& str) const
{
str << Token::LEFT_BRACE << Token::NEWLINE << Token::INDENT_INC;
for (vector<Statement*>::const_reverse_iterator i = m_children.rbegin(); i != m_children.rend(); i++)
(*i)->tokenize(state, str);
str << Token::INDENT_DEC << Token::RIGHT_BRACE << Token::NEWLINE;
}
void BlockStatement::execute (ExecutionContext& execCtx) const
{
for (vector<Statement*>::const_reverse_iterator i = m_children.rbegin(); i != m_children.rend(); i++)
(*i)->execute(execCtx);
}
void ExpressionStatement::tokenize (GeneratorState& state, TokenStream& str) const
{
DE_ASSERT(m_expression);
m_expression->tokenize(state, str);
str << Token::SEMICOLON << Token::NEWLINE;
}
namespace
{
inline bool canDeclareVariable (const Variable* variable)
{
return variable->getStorage() == Variable::STORAGE_LOCAL;
}
bool hasDeclarableVars (const VariableManager& varMgr)
{
const vector<Variable*>& liveVars = varMgr.getLiveVariables();
for (vector<Variable*>::const_iterator i = liveVars.begin(); i != liveVars.end(); i++)
{
if (canDeclareVariable(*i))
return true;
}
return false;
}
} // anonymous
DeclarationStatement::DeclarationStatement (GeneratorState& state, Variable* variable)
: m_variable (DE_NULL)
, m_expression (DE_NULL)
{
if (variable == DE_NULL)
{
// Choose random
// \todo [2011-02-03 pyry] Allocate a new here?
// \todo [2011-05-26 pyry] Weights?
const vector<Variable*>& liveVars = state.getVariableManager().getLiveVariables();
vector<Variable*> candidates;
for (vector<Variable*>::const_iterator i = liveVars.begin(); i != liveVars.end(); i++)
{
if (canDeclareVariable(*i))
candidates.push_back(*i);
}
variable = state.getRandom().choose<Variable*>(candidates.begin(), candidates.end());
}
DE_ASSERT(variable);
m_variable = variable;
const ValueEntry* value = state.getVariableManager().getValue(variable);
bool createInitializer = false;
switch (m_variable->getStorage())
{
case Variable::STORAGE_CONST:
DE_ASSERT(value);
createInitializer = true;
break;
case Variable::STORAGE_LOCAL:
// \note Currently booleans are always treated as not having undefined range and thus
// initializer is always created.
createInitializer = value && !isUndefinedValueRange(value->getValueRange());
break;
default:
createInitializer = false;
break;
}
if (createInitializer)
{
ExpressionGenerator generator(state);
// Take copy of value range for generating initializer expression
ValueRange valueRange = value->getValueRange();
// Declare (removes value entry)
state.getVariableManager().declareVariable(variable);
bool isConst = m_variable->getStorage() == Variable::STORAGE_CONST;
if (isConst)
state.pushExpressionFlags(state.getExpressionFlags() | CONST_EXPR);
m_expression = generator.generate(valueRange, 1);
if (isConst)
state.popExpressionFlags();
}
else
state.getVariableManager().declareVariable(variable);
}
DeclarationStatement::~DeclarationStatement (void)
{
delete m_expression;
}
float DeclarationStatement::getWeight (const GeneratorState& state)
{
if (!hasDeclarableVars(state.getVariableManager()))
return 0.0f;
if (!isCurrentTopStatementBlock(state))
return 0.0f;
return state.getProgramParameters().declarationStatementBaseWeight;
}
void DeclarationStatement::tokenize (GeneratorState& state, TokenStream& str) const
{
m_variable->tokenizeDeclaration(state, str);
if (m_expression)
{
str << Token::EQUAL;
m_expression->tokenize(state, str);
}
str << Token::SEMICOLON << Token::NEWLINE;
}
void DeclarationStatement::execute (ExecutionContext& execCtx) const
{
if (m_expression)
{
m_expression->evaluate(execCtx);
execCtx.getValue(m_variable) = m_expression->getValue().value();
}
}
ConditionalStatement::ConditionalStatement (GeneratorState&)
: m_condition (DE_NULL)
, m_trueStatement (DE_NULL)
, m_falseStatement (DE_NULL)
{
}
ConditionalStatement::~ConditionalStatement (void)
{
delete m_condition;
delete m_trueStatement;
delete m_falseStatement;
}
bool ConditionalStatement::isElseBlockRequired (const GeneratorState& state) const
{
// If parent is conditional statement with else block and this is the true statement,
// else block must be generated or otherwise parent "else" will end up parsed as else statement for this if.
const ConditionalStatement* curChild = this;
int curStackNdx = state.getStatementDepth()-2;
while (curStackNdx >= 0)
{
const ConditionalStatement* curParent = dynamic_cast<const ConditionalStatement*>(state.getStatementStackEntry(curStackNdx));
if (!curParent)
break; // Not a conditional statement - can end search here.
if (curChild == curParent->m_trueStatement && curParent->m_falseStatement)
return true; // Else block is mandatory.
// Continue search.
curChild = curParent;
curStackNdx -= 1;
}
return false;
}
Statement* ConditionalStatement::createNextChild (GeneratorState& state)
{
// If has neither true or false statements, choose randomly whether to create false block.
if (!m_falseStatement && !m_trueStatement && (isElseBlockRequired(state) || state.getRandom().getBool()))
{
// Construct false statement
state.getVariableManager().pushValueScope(m_conditionalScope);
m_falseStatement = createStatement(state);
return m_falseStatement;
}
else if (!m_trueStatement)
{
if (m_falseStatement)
{
// Pop previous value scope.
state.getVariableManager().popValueScope();
m_conditionalScope.clear();
}
// Construct true statement
state.getVariableManager().pushValueScope(m_conditionalScope);
m_trueStatement = createStatement(state);
return m_trueStatement;
}
else
{
// Pop conditional scope.
state.getVariableManager().popValueScope();
m_conditionalScope.clear();
// Create condition
DE_ASSERT(!m_condition);
ExpressionGenerator generator(state);
ValueRange range = ValueRange(VariableType::getScalarType(VariableType::TYPE_BOOL));
range.getMin().asBool() = false;
range.getMax().asBool() = true;
m_condition = generator.generate(range, 1);
return DE_NULL; // Done with this statement
}
}
namespace
{
bool isBlockStatement (const Statement* statement)
{
return dynamic_cast<const BlockStatement*>(statement) != DE_NULL;
}
bool isConditionalStatement (const Statement* statement)
{
return dynamic_cast<const ConditionalStatement*>(statement) != DE_NULL;
}
} // anonymous
void ConditionalStatement::tokenize (GeneratorState& state, TokenStream& str) const
{
DE_ASSERT(m_condition && m_trueStatement);
// if (condition)
str << Token::IF << Token::LEFT_PAREN;
m_condition->tokenize(state, str);
str << Token::RIGHT_PAREN << Token::NEWLINE;
// Statement executed if true
if (!isBlockStatement(m_trueStatement))
{
str << Token::INDENT_INC;
m_trueStatement->tokenize(state, str);
str << Token::INDENT_DEC;
}
else
m_trueStatement->tokenize(state, str);
if (m_falseStatement)
{
str << Token::ELSE;
if (isConditionalStatement(m_falseStatement))
{
m_falseStatement->tokenize(state, str);
}
else if (isBlockStatement(m_falseStatement))
{
str << Token::NEWLINE;
m_falseStatement->tokenize(state, str);
}
else
{
str << Token::NEWLINE << Token::INDENT_INC;
m_falseStatement->tokenize(state, str);
str << Token::INDENT_DEC;
}
}
}
void ConditionalStatement::execute (ExecutionContext& execCtx) const
{
// Evaluate condition
m_condition->evaluate(execCtx);
ExecMaskStorage maskStorage; // Value might change when we are evaluating true block so we have to take a copy.
ExecValueAccess trueMask = maskStorage.getValue();
trueMask = m_condition->getValue().value();
// And mask, execute true statement and pop
execCtx.andExecutionMask(trueMask);
m_trueStatement->execute(execCtx);
execCtx.popExecutionMask();
if (m_falseStatement)
{
// Construct negated mask, execute false statement and pop
ExecMaskStorage tmp;
ExecValueAccess falseMask = tmp.getValue();
for (int i = 0; i < EXEC_VEC_WIDTH; i++)
falseMask.asBool(i) = !trueMask.asBool(i);
execCtx.andExecutionMask(falseMask);
m_falseStatement->execute(execCtx);
execCtx.popExecutionMask();
}
}
float ConditionalStatement::getWeight (const GeneratorState& state)
{
if (!state.getProgramParameters().useConditionals)
return 0.0f;
int availableLevels = state.getShaderParameters().maxStatementDepth - state.getStatementDepth();
return (availableLevels > 1) ? 1.0f : 0.0f;
}
AssignStatement::AssignStatement (const Variable* variable, Expression* value)
: m_variable (variable)
, m_valueExpr (value) // \note Takes ownership of value
{
}
AssignStatement::AssignStatement (GeneratorState& state, const Variable* variable, ConstValueRangeAccess valueRange)
: m_variable (variable)
, m_valueExpr (DE_NULL)
{
// Generate random value
ExpressionGenerator generator(state);
m_valueExpr = generator.generate(valueRange, 1);
}
AssignStatement::~AssignStatement (void)
{
delete m_valueExpr;
}
void AssignStatement::tokenize (GeneratorState& state, TokenStream& str) const
{
str << Token(m_variable->getName()) << Token::EQUAL;
m_valueExpr->tokenize(state, str);
str << Token::SEMICOLON << Token::NEWLINE;
}
void AssignStatement::execute (ExecutionContext& execCtx) const
{
m_valueExpr->evaluate(execCtx);
assignMasked(execCtx.getValue(m_variable), m_valueExpr->getValue(), execCtx.getExecutionMask());
}
} // rsg