| /*------------------------------------------------------------------------- |
| * 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 |