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