You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
505 lines
14 KiB
505 lines
14 KiB
/*-------------------------------------------------------------------------
|
|
* 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::vector;
|
|
using std::set;
|
|
using std::map;
|
|
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // rsg
|