| /*------------------------------------------------------------------------- |
| * 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 Variable manager. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "rsgVariableManager.hpp" |
| |
| #include <algorithm> |
| #include <map> |
| #include <set> |
| |
| using std::map; |
| using std::set; |
| using std::vector; |
| |
| namespace rsg |
| { |
| |
| class SubValueRangeIterator |
| { |
| public: |
| SubValueRangeIterator(const ConstValueRangeAccess &valueRange); |
| ~SubValueRangeIterator(void) |
| { |
| } |
| |
| bool hasItem(void) const; |
| ConstValueRangeAccess getItem(void) const; |
| void next(void); |
| |
| private: |
| vector<ConstValueRangeAccess> m_stack; |
| }; |
| |
| SubValueRangeIterator::SubValueRangeIterator(const ConstValueRangeAccess &valueRange) |
| { |
| m_stack.push_back(valueRange); |
| } |
| |
| inline bool SubValueRangeIterator::hasItem(void) const |
| { |
| return !m_stack.empty(); |
| } |
| |
| inline ConstValueRangeAccess SubValueRangeIterator::getItem(void) const |
| { |
| return m_stack[m_stack.size() - 1]; |
| } |
| |
| void SubValueRangeIterator::next(void) |
| { |
| ConstValueRangeAccess curItem = getItem(); |
| m_stack.pop_back(); // Remove current |
| |
| switch (curItem.getType().getBaseType()) |
| { |
| case VariableType::TYPE_ARRAY: |
| { |
| int numElements = curItem.getType().getNumElements(); |
| for (int ndx = 0; ndx < numElements; ndx++) |
| m_stack.push_back(curItem.member(ndx)); |
| break; |
| } |
| |
| case VariableType::TYPE_STRUCT: |
| { |
| int numMembers = (int)curItem.getType().getMembers().size(); |
| for (int ndx = 0; ndx < numMembers; ndx++) |
| m_stack.push_back(curItem.member(ndx)); |
| break; |
| } |
| |
| default: |
| break; // \todo [2011-02-03 pyry] Swizzle control? |
| } |
| } |
| |
| ValueEntry::ValueEntry(const Variable *variable) : m_variable(variable), m_valueRange(variable->getType()) |
| { |
| } |
| |
| VariableScope::VariableScope(void) |
| { |
| } |
| |
| VariableScope::~VariableScope(void) |
| { |
| for (vector<Variable *>::iterator i = m_declaredVariables.begin(); i != m_declaredVariables.end(); i++) |
| delete *i; |
| |
| for (vector<Variable *>::iterator i = m_liveVariables.begin(); i != m_liveVariables.end(); i++) |
| delete *i; |
| } |
| |
| Variable *VariableScope::allocate(const VariableType &type, Variable::Storage storage, const char *name) |
| { |
| Variable *variable = new Variable(type, storage, name); |
| try |
| { |
| m_liveVariables.push_back(variable); |
| return variable; |
| } |
| catch (const std::exception &) |
| { |
| delete variable; |
| throw; |
| } |
| } |
| |
| void VariableScope::declare(Variable *variable) |
| { |
| m_declaredVariables.push_back(variable); |
| removeLive(variable); |
| } |
| |
| void VariableScope::removeLive(const Variable *variable) |
| { |
| vector<Variable *>::iterator pos = std::find(m_liveVariables.begin(), m_liveVariables.end(), variable); |
| DE_ASSERT(pos != m_liveVariables.end()); |
| |
| // \todo [pyry] Not so efficient |
| m_liveVariables.erase(pos); |
| } |
| |
| ValueScope::ValueScope(void) |
| { |
| } |
| |
| ValueScope::~ValueScope(void) |
| { |
| clear(); |
| } |
| |
| void ValueScope::clear(void) |
| { |
| for (vector<ValueEntry *>::iterator i = m_entries.begin(); i != m_entries.end(); i++) |
| delete *i; |
| m_entries.clear(); |
| } |
| |
| ValueEntry *ValueScope::allocate(const Variable *variable) |
| { |
| ValueEntry *entry = new ValueEntry(variable); |
| try |
| { |
| m_entries.push_back(entry); |
| return entry; |
| } |
| catch (const std::exception &) |
| { |
| delete entry; |
| throw; |
| } |
| } |
| |
| class CompareEntryVariable |
| { |
| public: |
| CompareEntryVariable(const Variable *variable) : m_variable(variable) |
| { |
| } |
| |
| bool operator==(const ValueEntry *entry) const |
| { |
| return entry->getVariable() == m_variable; |
| } |
| |
| private: |
| const Variable *m_variable; |
| }; |
| |
| bool operator==(const ValueEntry *entry, const CompareEntryVariable &cmp) |
| { |
| return cmp == entry; |
| } |
| |
| ValueEntry *ValueScope::findEntry(const Variable *variable) const |
| { |
| vector<ValueEntry *>::const_iterator pos = |
| std::find(m_entries.begin(), m_entries.end(), CompareEntryVariable(variable)); |
| return pos != m_entries.end() ? *pos : DE_NULL; |
| } |
| |
| void ValueScope::setValue(const Variable *variable, ConstValueRangeAccess value) |
| { |
| ValueEntry *entry = findEntry(variable); |
| DE_ASSERT(entry); |
| |
| ValueRangeAccess dst = entry->getValueRange(); |
| dst.getMin() = value.getMin().value(); |
| dst.getMax() = value.getMax().value(); |
| } |
| |
| void ValueScope::removeValue(const Variable *variable) |
| { |
| vector<ValueEntry *>::iterator pos = std::find(m_entries.begin(), m_entries.end(), CompareEntryVariable(variable)); |
| if (pos != m_entries.end()) |
| { |
| ValueEntry *entry = *pos; |
| m_entries.erase(pos); |
| delete entry; |
| } |
| } |
| |
| VariableManager::VariableManager(NameAllocator &nameAllocator) |
| : m_numAllocatedScalars(0) |
| , m_numAllocatedShaderInScalars(0) |
| , m_numAllocatedShaderInVariables(0) |
| , m_numAllocatedUniformScalars(0) |
| , m_nameAllocator(nameAllocator) |
| { |
| } |
| |
| VariableManager::~VariableManager(void) |
| { |
| } |
| |
| Variable *VariableManager::allocate(const VariableType &type) |
| { |
| return allocate(type, Variable::STORAGE_LOCAL, m_nameAllocator.allocate().c_str()); |
| } |
| |
| Variable *VariableManager::allocate(const VariableType &type, Variable::Storage storage, const char *name) |
| { |
| VariableScope &varScope = getCurVariableScope(); |
| ValueScope &valueScope = getCurValueScope(); |
| int numScalars = type.getScalarSize(); |
| |
| // Allocate in current scope |
| Variable *variable = varScope.allocate(type, Variable::STORAGE_LOCAL, name); |
| |
| // Allocate value entry |
| ValueEntry *valueEntry = valueScope.allocate(variable); |
| |
| // Add to cache |
| m_entryCache.push_back(valueEntry); |
| |
| m_numAllocatedScalars += numScalars; |
| |
| // Set actual storage - affects uniform/shader in allocations. |
| setStorage(variable, storage); |
| |
| return variable; |
| } |
| |
| void VariableManager::setStorage(Variable *variable, Variable::Storage storage) |
| { |
| int numScalars = variable->getType().getScalarSize(); |
| |
| // Decrement old. |
| if (variable->getStorage() == Variable::STORAGE_SHADER_IN) |
| { |
| m_numAllocatedShaderInScalars -= numScalars; |
| m_numAllocatedShaderInVariables -= 1; |
| } |
| else if (variable->getStorage() == Variable::STORAGE_UNIFORM) |
| m_numAllocatedUniformScalars -= numScalars; |
| |
| // Add new. |
| if (storage == Variable::STORAGE_SHADER_IN) |
| { |
| m_numAllocatedShaderInScalars += numScalars; |
| m_numAllocatedShaderInVariables += 1; |
| } |
| else if (storage == Variable::STORAGE_UNIFORM) |
| m_numAllocatedUniformScalars += numScalars; |
| |
| variable->setStorage(storage); |
| } |
| |
| bool VariableManager::canDeclareInCurrentScope(const Variable *variable) const |
| { |
| const vector<Variable *> &curLiveVars = getCurVariableScope().getLiveVariables(); |
| return std::find(curLiveVars.begin(), curLiveVars.end(), variable) != curLiveVars.end(); |
| } |
| |
| const vector<Variable *> &VariableManager::getLiveVariables(void) const |
| { |
| return getCurVariableScope().getLiveVariables(); |
| } |
| |
| void VariableManager::declareVariable(Variable *variable) |
| { |
| // Remove from cache if exists in there. |
| std::vector<const ValueEntry *>::iterator pos = |
| std::find(m_entryCache.begin(), m_entryCache.end(), CompareEntryVariable(variable)); |
| if (pos != m_entryCache.end()) |
| m_entryCache.erase(pos); |
| |
| DE_ASSERT(std::find(m_entryCache.begin(), m_entryCache.end(), CompareEntryVariable(variable)) == |
| m_entryCache.end()); |
| |
| // Remove from scope stack. |
| for (vector<ValueScope *>::const_iterator stackIter = m_valueScopeStack.begin(); |
| stackIter != m_valueScopeStack.end(); stackIter++) |
| { |
| ValueScope *scope = *stackIter; |
| scope->removeValue(variable); |
| } |
| |
| // Declare in current scope. |
| getCurVariableScope().declare(variable); |
| } |
| |
| const ValueEntry *VariableManager::getValue(const Variable *variable) const |
| { |
| vector<const ValueEntry *>::const_iterator pos = |
| std::find(m_entryCache.begin(), m_entryCache.end(), CompareEntryVariable(variable)); |
| return pos != m_entryCache.end() ? *pos : DE_NULL; |
| } |
| |
| void VariableManager::removeValueFromCurrentScope(const Variable *variable) |
| { |
| // Remove from cache |
| std::vector<const ValueEntry *>::iterator pos = |
| std::find(m_entryCache.begin(), m_entryCache.end(), CompareEntryVariable(variable)); |
| DE_ASSERT(pos != m_entryCache.end()); |
| m_entryCache.erase(pos); |
| |
| // Remove from current scope \note May not exist in there. |
| getCurValueScope().removeValue(variable); |
| } |
| |
| const ValueEntry *VariableManager::getParentValue(const Variable *variable) const |
| { |
| if (m_valueScopeStack.size() < 2) |
| return DE_NULL; // Only single value scope |
| |
| for (vector<ValueScope *>::const_reverse_iterator i = m_valueScopeStack.rbegin() + 1; i != m_valueScopeStack.rend(); |
| i++) |
| { |
| const ValueScope *scope = *i; |
| ValueEntry *entry = scope->findEntry(variable); |
| |
| if (entry) |
| return entry; |
| } |
| |
| return DE_NULL; // Not found in stack |
| } |
| |
| void VariableManager::setValue(const Variable *variable, ConstValueRangeAccess value) |
| { |
| ValueScope &curScope = getCurValueScope(); |
| |
| if (!curScope.findEntry(variable)) |
| { |
| // New value, allocate and update cache. |
| ValueEntry *newEntry = curScope.allocate(variable); |
| std::vector<const ValueEntry *>::iterator cachePos = |
| std::find(m_entryCache.begin(), m_entryCache.end(), CompareEntryVariable(variable)); |
| |
| if (cachePos != m_entryCache.end()) |
| *cachePos = newEntry; |
| else |
| m_entryCache.push_back(newEntry); |
| } |
| |
| curScope.setValue(variable, value); |
| } |
| |
| void VariableManager::reserve(ReservedScalars &store, int numScalars) |
| { |
| DE_ASSERT(store.numScalars == 0); |
| store.numScalars = numScalars; |
| m_numAllocatedScalars += numScalars; |
| } |
| |
| void VariableManager::release(ReservedScalars &store) |
| { |
| m_numAllocatedScalars -= store.numScalars; |
| store.numScalars = 0; |
| } |
| |
| void VariableManager::pushVariableScope(VariableScope &scope) |
| { |
| // Expects emtpy scope |
| DE_ASSERT(scope.getDeclaredVariables().size() == 0); |
| DE_ASSERT(scope.getLiveVariables().size() == 0); |
| |
| m_variableScopeStack.push_back(&scope); |
| } |
| |
| void VariableManager::popVariableScope(void) |
| { |
| VariableScope &curScope = getCurVariableScope(); |
| |
| // Migrate live variables to parent scope. |
| // Variables allocated in child scopes can be declared in any parent scope but not the other way around. |
| if (m_variableScopeStack.size() > 1) |
| { |
| VariableScope &parentScope = *m_variableScopeStack[m_variableScopeStack.size() - 2]; |
| vector<Variable *> &curLiveVars = curScope.getLiveVariables(); |
| vector<Variable *> &parenLiveVars = parentScope.getLiveVariables(); |
| |
| while (!curLiveVars.empty()) |
| { |
| Variable *liveVar = curLiveVars.back(); |
| parenLiveVars.push_back(liveVar); |
| curLiveVars.pop_back(); |
| } |
| } |
| |
| // All variables should be either migrated to parent or declared (in case of root scope). |
| DE_ASSERT(curScope.getLiveVariables().size() == 0); |
| |
| m_variableScopeStack.pop_back(); |
| } |
| |
| void VariableManager::pushValueScope(ValueScope &scope) |
| { |
| // Value scope should be empty |
| DE_ASSERT(scope.getValues().size() == 0); |
| |
| m_valueScopeStack.push_back(&scope); |
| } |
| |
| void VariableManager::popValueScope(void) |
| { |
| ValueScope &oldScope = getCurValueScope(); |
| |
| // Pop scope and clear cache. |
| m_valueScopeStack.pop_back(); |
| m_entryCache.clear(); |
| |
| // Re-build entry cache. |
| if (!m_valueScopeStack.empty()) |
| { |
| ValueScope &newTopScope = getCurValueScope(); |
| |
| // Speed up computing intersections. |
| map<const Variable *, const ValueEntry *> oldValues; |
| const vector<ValueEntry *> &oldEntries = oldScope.getValues(); |
| |
| for (vector<ValueEntry *>::const_iterator valueIter = oldEntries.begin(); valueIter != oldEntries.end(); |
| valueIter++) |
| oldValues[(*valueIter)->getVariable()] = *valueIter; |
| |
| set<const Variable *> addedVars; |
| |
| // Re-build based on current stack. |
| for (vector<ValueScope *>::reverse_iterator scopeIter = m_valueScopeStack.rbegin(); |
| scopeIter != m_valueScopeStack.rend(); scopeIter++) |
| { |
| const ValueScope *scope = *scopeIter; |
| const vector<ValueEntry *> &valueEntries = scope->getValues(); |
| |
| for (vector<ValueEntry *>::const_iterator valueIter = valueEntries.begin(); valueIter != valueEntries.end(); |
| valueIter++) |
| { |
| const ValueEntry *entry = *valueIter; |
| const Variable *var = entry->getVariable(); |
| |
| if (addedVars.find(var) != addedVars.end()) |
| continue; // Already in cache, set deeper in scope stack. |
| |
| DE_ASSERT(std::find(m_entryCache.begin(), m_entryCache.end(), CompareEntryVariable(var)) == |
| m_entryCache.end()); |
| |
| if (oldValues.find(var) != oldValues.end()) |
| { |
| const ValueEntry *oldEntry = oldValues[var]; |
| |
| // Build new intersected value and store into current scope. |
| ValueRange intersectedValue(var->getType()); |
| DE_ASSERT(oldEntry->getValueRange().intersects(entry->getValueRange())); // Must intersect |
| ValueRange::computeIntersection(intersectedValue, entry->getValueRange(), |
| oldEntry->getValueRange()); |
| |
| if (!newTopScope.findEntry(var)) |
| newTopScope.allocate(var); |
| |
| newTopScope.setValue(var, intersectedValue.asAccess()); |
| |
| // Add entry from top scope to cache. |
| m_entryCache.push_back(newTopScope.findEntry(var)); |
| } |
| else |
| m_entryCache.push_back(entry); // Just add to cache. |
| |
| addedVars.insert(var); // Record as cached variable. |
| } |
| } |
| |
| // Copy entries from popped scope that don't yet exist in the stack. |
| for (vector<ValueEntry *>::const_iterator valueIter = oldEntries.begin(); valueIter != oldEntries.end(); |
| valueIter++) |
| { |
| const ValueEntry *oldEntry = *valueIter; |
| const Variable *var = oldEntry->getVariable(); |
| |
| if (addedVars.find(var) == addedVars.end()) |
| setValue(var, oldEntry->getValueRange()); |
| } |
| } |
| } |
| |
| } // namespace rsg |