/*------------------------------------------------------------------------- * 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 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(DE_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::Iterator end = varManager.getEnd(); 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 entries; std::copy(state.getVariableManager().getBegin(), state.getVariableManager().getEnd(), std::inserter(entries, entries.begin())); for (vector::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(DE_FALSE); } } } } void fillUndefinedShaderInputs (vector& inputs) { for (vector::iterator i = inputs.begin(); i != inputs.end(); i++) { if (!(*i)->getVariable()->getType().isSampler()) // Samplers are assigned at program-level. fillUndefinedComponents((*i)->getValueRange()); } } } // anonymous void ShaderGenerator::generate (const ShaderParameters& shaderParams, Shader& shader, const vector& 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::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& liveVars = globalVariableScope.getLiveVariables(); for (vector::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 liveVariables; std::copy(globalVariableScope.getLiveVariables().begin(), globalVariableScope.getLiveVariables().end(), std::inserter(liveVariables, liveVariables.begin())); vector createDeclarationStatementVars; for (vector::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& inputs = shader.getInputs(); vector& 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::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 dummy 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()); } } } // rsg