blob: 23e6c04e0adc33828133881da5efbc0363df528e [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 Binary ops.
*//*--------------------------------------------------------------------*/
#include "rsgBinaryOps.hpp"
#include "rsgVariableManager.hpp"
#include "rsgUtils.hpp"
#include "deMath.h"
using std::vector;
namespace rsg
{
// CustomAbsOp and CustomBinaryOp are used to resolve float comparision corner case.
// This error happened when two floats with the same value were compared
// without using epsilon. If result of this comparisment influenced the
// output color then result and reference images could differ.
class CustomAbsOp : public Expression
{
public:
CustomAbsOp(void);
virtual ~CustomAbsOp(void);
void setChild(Expression *expression);
Expression *createNextChild(GeneratorState &state);
void tokenize(GeneratorState &state, TokenStream &str) const;
void evaluate(ExecutionContext &execCtx);
ExecConstValueAccess getValue(void) const
{
return m_value.getValue(m_type);
}
private:
std::string m_function;
VariableType m_type;
ExecValueStorage m_value;
Expression *m_child;
};
CustomAbsOp::CustomAbsOp(void) : m_function("abs"), m_type(VariableType::TYPE_FLOAT, 1), m_child(DE_NULL)
{
m_value.setStorage(m_type);
}
CustomAbsOp::~CustomAbsOp(void)
{
delete m_child;
}
void CustomAbsOp::setChild(Expression *expression)
{
m_child = expression;
}
Expression *CustomAbsOp::createNextChild(GeneratorState &)
{
DE_ASSERT(0);
return DE_NULL;
}
void CustomAbsOp::tokenize(GeneratorState &state, TokenStream &str) const
{
str << Token(m_function.c_str()) << Token::LEFT_PAREN;
m_child->tokenize(state, str);
str << Token::RIGHT_PAREN;
}
void CustomAbsOp::evaluate(ExecutionContext &execCtx)
{
m_child->evaluate(execCtx);
ExecConstValueAccess srcValue = m_child->getValue();
ExecValueAccess dstValue = m_value.getValue(m_type);
for (int elemNdx = 0; elemNdx < m_type.getNumElements(); elemNdx++)
{
ExecConstValueAccess srcComp = srcValue.component(elemNdx);
ExecValueAccess dstComp = dstValue.component(elemNdx);
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dstComp.asFloat(compNdx) = deFloatAbs(srcComp.asFloat(compNdx));
}
}
typedef BinaryOp<5, ASSOCIATIVITY_LEFT> CustomBinaryBase;
// CustomBinaryOp and CustomAbsOp are used to resolve float comparision corner case.
// CustomBinaryOp supports addition and substraction as only those functionalities
// were needed.
template <typename ComputeValue>
class CustomBinaryOp : public CustomBinaryBase
{
public:
CustomBinaryOp();
virtual ~CustomBinaryOp(void)
{
}
void setLeftValue(Expression *expression);
void setRightValue(Expression *expression);
void evaluate(ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b);
};
template <typename ComputeValue>
CustomBinaryOp<ComputeValue>::CustomBinaryOp() : CustomBinaryBase(Token::PLUS)
{
// By default add operation is assumed, for every other operation
// separate constructor specialization should be implemented
m_type = VariableType(VariableType::TYPE_FLOAT, 1);
m_value.setStorage(m_type);
}
template <>
CustomBinaryOp<EvaluateSub>::CustomBinaryOp() : CustomBinaryBase(Token::MINUS)
{
// Specialization for substraction
m_type = VariableType(VariableType::TYPE_FLOAT, 1);
m_leftValueRange = ValueRange(m_type);
m_rightValueRange = ValueRange(m_type);
m_value.setStorage(m_type);
}
template <>
CustomBinaryOp<EvaluateLessThan>::CustomBinaryOp() : CustomBinaryBase(Token::CMP_LT)
{
// Specialization for less_then comparision
m_type = VariableType(VariableType::TYPE_BOOL, 1);
VariableType floatType = VariableType(VariableType::TYPE_FLOAT, 1);
m_leftValueRange = ValueRange(floatType);
m_rightValueRange = ValueRange(floatType);
m_value.setStorage(m_type);
}
template <typename ComputeValue>
void CustomBinaryOp<ComputeValue>::setLeftValue(Expression *expression)
{
m_leftValueExpr = expression;
}
template <typename ComputeValue>
void CustomBinaryOp<ComputeValue>::setRightValue(Expression *expression)
{
m_rightValueExpr = expression;
}
template <typename ComputeValue>
void CustomBinaryOp<ComputeValue>::evaluate(ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
{
DE_ASSERT(dst.getType() == a.getType());
DE_ASSERT(dst.getType() == b.getType());
DE_ASSERT(dst.getType().getBaseType() == VariableType::TYPE_FLOAT);
for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
{
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dst.component(elemNdx).asFloat(compNdx) =
ComputeValue()(a.component(elemNdx).asFloat(compNdx), b.component(elemNdx).asFloat(compNdx));
}
}
template <>
void CustomBinaryOp<EvaluateLessThan>::evaluate(ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
{
DE_ASSERT(a.getType() == b.getType());
DE_ASSERT(dst.getType().getBaseType() == VariableType::TYPE_BOOL);
for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
{
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dst.component(elemNdx).asBool(compNdx) =
EvaluateLessThan()(a.component(elemNdx).asFloat(compNdx), b.component(elemNdx).asFloat(compNdx));
}
}
template <int Precedence, Associativity Assoc>
BinaryOp<Precedence, Assoc>::BinaryOp(Token::Type operatorToken)
: m_operator(operatorToken)
, m_leftValueRange(m_type)
, m_rightValueRange(m_type)
, m_leftValueExpr(DE_NULL)
, m_rightValueExpr(DE_NULL)
{
}
template <int Precedence, Associativity Assoc>
BinaryOp<Precedence, Assoc>::~BinaryOp(void)
{
delete m_leftValueExpr;
delete m_rightValueExpr;
}
template <int Precedence, Associativity Assoc>
Expression *BinaryOp<Precedence, Assoc>::createNextChild(GeneratorState &state)
{
int leftPrec = Assoc == ASSOCIATIVITY_LEFT ? Precedence : Precedence - 1;
int rightPrec = Assoc == ASSOCIATIVITY_LEFT ? Precedence - 1 : Precedence;
if (m_rightValueExpr == DE_NULL)
{
state.pushPrecedence(rightPrec);
m_rightValueExpr = Expression::createRandom(state, m_rightValueRange.asAccess());
state.popPrecedence();
return m_rightValueExpr;
}
else if (m_leftValueExpr == DE_NULL)
{
state.pushPrecedence(leftPrec);
m_leftValueExpr = Expression::createRandom(state, m_leftValueRange.asAccess());
state.popPrecedence();
return m_leftValueExpr;
}
else
{
// Check for corrner cases
switch (m_operator)
{
case Token::CMP_LE:
{
// When comparing two floats epsilon should be included
// to eliminate the risk that we get different results
// because of precission error
VariableType floatType(VariableType::TYPE_FLOAT, 1);
if (m_rightValueRange.getType() == floatType)
{
FloatLiteral *epsilonLiteral = new FloatLiteral(0.001f);
typedef CustomBinaryOp<EvaluateAdd> CustomAddOp;
CustomAddOp *addOperation = new CustomAddOp();
addOperation->setLeftValue(m_rightValueExpr);
addOperation->setRightValue(epsilonLiteral);
// add epsilon to right-hand side
m_rightValueExpr = addOperation;
}
break;
}
case Token::CMP_GE:
{
// When comparing two floats epsilon should be included
// to eliminate the risk that we get different results
// because of precission error
VariableType floatType(VariableType::TYPE_FLOAT, 1);
if (m_leftValueRange.getType() == floatType)
{
FloatLiteral *epsilonLiteral = new FloatLiteral(0.001f);
typedef CustomBinaryOp<EvaluateAdd> CustomAddOp;
CustomAddOp *addOperation = new CustomAddOp();
addOperation->setLeftValue(m_leftValueExpr);
addOperation->setRightValue(epsilonLiteral);
// add epsilon to left-hand side
m_leftValueExpr = addOperation;
}
break;
}
case Token::CMP_EQ:
{
// When comparing two floats epsilon should be included
// to eliminate the risk that we get different results
// because of precission error
VariableType floatType(VariableType::TYPE_FLOAT, 1);
if (m_leftValueRange.getType() == floatType)
{
VariableType boolType(VariableType::TYPE_BOOL, 1);
const ValueRange boolRange(boolType);
ParenOp *parenRight = new ParenOp(state, boolRange);
parenRight->setChild(m_rightValueExpr);
typedef CustomBinaryOp<EvaluateSub> CustomSubOp;
CustomSubOp *subOperation = new CustomSubOp();
subOperation->setLeftValue(m_leftValueExpr);
subOperation->setRightValue(parenRight);
CustomAbsOp *absOperation = new CustomAbsOp();
absOperation->setChild(subOperation);
FloatLiteral *epsilonLiteral = new FloatLiteral(0.001f);
typedef CustomBinaryOp<EvaluateLessThan> CustomLessThanOp;
CustomLessThanOp *lessOperation = new CustomLessThanOp();
lessOperation->setLeftValue(absOperation);
lessOperation->setRightValue(epsilonLiteral);
ParenOp *parenOperation = new ParenOp(state, boolRange);
parenOperation->setChild(lessOperation);
BoolLiteral *trueLiteral = new BoolLiteral(true);
// EQ operation cant be removed so it is replaced with:
// ((abs(lhs-rhs) < epsilon) == true).
m_leftValueExpr = parenOperation;
m_rightValueExpr = trueLiteral;
}
break;
}
default:
break;
}
return DE_NULL;
}
}
template <int Precedence, Associativity Assoc>
float BinaryOp<Precedence, Assoc>::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
if (state.getPrecedence() < Precedence)
return 0.0f;
int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth();
if (valueRange.getType().isVoid())
return availableLevels >= 2 ? unusedValueWeight : 0.0f;
if (availableLevels < getConservativeValueExprDepth(state, valueRange) + 1)
return 0.0f;
return 1.0f;
}
template <int Precedence, Associativity Assoc>
void BinaryOp<Precedence, Assoc>::tokenize(GeneratorState &state, TokenStream &str) const
{
m_leftValueExpr->tokenize(state, str);
str << m_operator;
m_rightValueExpr->tokenize(state, str);
}
template <int Precedence, Associativity Assoc>
void BinaryOp<Precedence, Assoc>::evaluate(ExecutionContext &execCtx)
{
m_leftValueExpr->evaluate(execCtx);
m_rightValueExpr->evaluate(execCtx);
ExecConstValueAccess leftVal = m_leftValueExpr->getValue();
ExecConstValueAccess rightVal = m_rightValueExpr->getValue();
ExecValueAccess dst = m_value.getValue(m_type);
evaluate(dst, leftVal, rightVal);
}
template <int Precedence, bool Float, bool Int, bool Bool, class ComputeValueRange, class EvaluateComp>
BinaryVecOp<Precedence, Float, Int, Bool, ComputeValueRange, EvaluateComp>::BinaryVecOp(
GeneratorState &state, Token::Type operatorToken, ConstValueRangeAccess inValueRange)
: BinaryOp<Precedence, ASSOCIATIVITY_LEFT>(operatorToken)
{
ValueRange valueRange = inValueRange;
if (valueRange.getType().isVoid())
{
int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth();
vector<VariableType::Type> baseTypes;
if (Float)
baseTypes.push_back(VariableType::TYPE_FLOAT);
if (Int)
baseTypes.push_back(VariableType::TYPE_INT);
if (Bool)
baseTypes.push_back(VariableType::TYPE_BOOL);
VariableType::Type baseType = state.getRandom().choose<VariableType::Type>(baseTypes.begin(), baseTypes.end());
int numElements = state.getRandom().getInt(1, availableLevels >= 3 ? 4 : 1);
valueRange = ValueRange(VariableType(baseType, numElements));
computeRandomValueRange(state, valueRange.asAccess());
}
// Choose type, allocate storage for execution
this->m_type = valueRange.getType();
this->m_value.setStorage(this->m_type);
// Initialize storage for value ranges
this->m_rightValueRange = ValueRange(this->m_type);
this->m_leftValueRange = ValueRange(this->m_type);
VariableType::Type baseType = this->m_type.getBaseType();
// Compute range for b that satisfies requested value range
for (int elemNdx = 0; elemNdx < this->m_type.getNumElements(); elemNdx++)
{
ConstValueRangeAccess dst = valueRange.asAccess().component(elemNdx);
ValueRangeAccess a = this->m_leftValueRange.asAccess().component(
elemNdx); // \todo [2011-03-25 pyry] Commutative: randomize inputs
ValueRangeAccess b = this->m_rightValueRange.asAccess().component(elemNdx);
// Just pass undefined ranges
if ((baseType == VariableType::TYPE_FLOAT || baseType == VariableType::TYPE_INT) && isUndefinedValueRange(dst))
{
a.getMin() = dst.getMin().value();
b.getMin() = dst.getMin().value();
a.getMax() = dst.getMax().value();
b.getMax() = dst.getMax().value();
continue;
}
if (baseType == VariableType::TYPE_FLOAT)
ComputeValueRange()(state.getRandom(), dst.getMin().asFloat(), dst.getMax().asFloat(), a.getMin().asFloat(),
a.getMax().asFloat(), b.getMin().asFloat(), b.getMax().asFloat());
else if (baseType == VariableType::TYPE_INT)
ComputeValueRange()(state.getRandom(), dst.getMin().asInt(), dst.getMax().asInt(), a.getMin().asInt(),
a.getMax().asInt(), b.getMin().asInt(), b.getMax().asInt());
else
{
DE_ASSERT(baseType == VariableType::TYPE_BOOL);
ComputeValueRange()(state.getRandom(), dst.getMin().asBool(), dst.getMax().asBool(), a.getMin().asBool(),
a.getMax().asBool(), b.getMin().asBool(), b.getMax().asBool());
}
}
}
template <int Precedence, bool Float, bool Int, bool Bool, class ComputeValueRange, class EvaluateComp>
BinaryVecOp<Precedence, Float, Int, Bool, ComputeValueRange, EvaluateComp>::~BinaryVecOp(void)
{
}
template <int Precedence, bool Float, bool Int, bool Bool, class ComputeValueRange, class EvaluateComp>
void BinaryVecOp<Precedence, Float, Int, Bool, ComputeValueRange, EvaluateComp>::evaluate(ExecValueAccess dst,
ExecConstValueAccess a,
ExecConstValueAccess b)
{
DE_ASSERT(dst.getType() == a.getType());
DE_ASSERT(dst.getType() == b.getType());
switch (dst.getType().getBaseType())
{
case VariableType::TYPE_FLOAT:
for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
{
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dst.component(elemNdx).asFloat(compNdx) =
EvaluateComp()(a.component(elemNdx).asFloat(compNdx), b.component(elemNdx).asFloat(compNdx));
}
break;
case VariableType::TYPE_INT:
for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
{
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dst.component(elemNdx).asInt(compNdx) =
EvaluateComp()(a.component(elemNdx).asInt(compNdx), b.component(elemNdx).asInt(compNdx));
}
break;
default:
DE_ASSERT(false); // Invalid type for multiplication
}
}
void ComputeMulRange::operator()(de::Random &rnd, float dstMin, float dstMax, float &aMin, float &aMax, float &bMin,
float &bMax) const
{
const float minScale = 0.25f;
const float maxScale = 2.0f;
const float subRangeStep = 0.25f;
const float scaleStep = 0.25f;
float scale = getQuantizedFloat(rnd, minScale, maxScale, scaleStep);
float scaledMin = dstMin / scale;
float scaledMax = dstMax / scale;
// Quantize scaled value range if possible
if (!quantizeFloatRange(scaledMin, scaledMax))
{
// Fall back to 1.0 as a scale
scale = 1.0f;
scaledMin = dstMin;
scaledMax = dstMax;
}
float subRangeLen = getQuantizedFloat(rnd, 0.0f, scaledMax - scaledMin, subRangeStep);
aMin = scaledMin + getQuantizedFloat(rnd, 0.0f, (scaledMax - scaledMin) - subRangeLen, subRangeStep);
aMax = aMin + subRangeLen;
// Find scale range
bMin = scale;
bMax = scale;
for (int i = 0; i < 5; i++)
{
if (de::inBounds(aMin * (scale - (float)i * scaleStep), dstMin, dstMax) &&
de::inBounds(aMax * (scale - (float)i * scaleStep), dstMin, dstMax))
bMin = scale - (float)i * scaleStep;
if (de::inBounds(aMin * (scale + (float)i * scaleStep), dstMin, dstMax) &&
de::inBounds(aMax * (scale + (float)i * scaleStep), dstMin, dstMax))
bMax = scale + (float)i * scaleStep;
}
// Negative scale?
if (rnd.getBool())
{
std::swap(aMin, aMax);
std::swap(bMin, bMax);
aMin *= -1.0f;
aMax *= -1.0f;
bMin *= -1.0f;
bMax *= -1.0f;
}
#if defined(DE_DEBUG)
const float eps = 0.001f;
DE_ASSERT(aMin <= aMax && bMin <= bMax);
DE_ASSERT(de::inRange(aMin * bMin, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMin * bMax, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMax * bMin, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMax * bMax, dstMin - eps, dstMax + eps));
#endif
}
void ComputeMulRange::operator()(de::Random &rnd, int dstMin, int dstMax, int &aMin, int &aMax, int &bMin,
int &bMax) const
{
DE_UNREF(rnd);
aMin = dstMin;
aMax = dstMax;
bMin = 1;
bMax = 1;
}
MulOp::MulOp(GeneratorState &state, ConstValueRangeAccess valueRange) : MulBase(state, Token::MUL, valueRange)
{
}
float MulOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
if (valueRange.getType().isVoid() || valueRange.getType().isFloatOrVec() || valueRange.getType().isIntOrVec())
return MulBase::getWeight(state, valueRange);
else
return 0.0f;
}
template <typename T>
void ComputeAddRange::operator()(de::Random &random, T dstMin, T dstMax, T &aMin, T &aMax, T &bMin, T &bMax) const
{
struct GetRandom
{
int operator()(de::Random &rnd, int min, int max) const
{
return rnd.getInt(min, max);
}
float operator()(de::Random &rnd, float min, float max) const
{
return getQuantizedFloat(rnd, min, max, 0.5f);
}
};
T rangeLen = dstMax - dstMin;
T subRangeLen = GetRandom()(random, T(0), rangeLen);
T aOffset = GetRandom()(random, T(-8), T(8));
aMin = dstMin + aOffset;
aMax = aMin + subRangeLen;
bMin = -aOffset;
bMax = -aOffset + (rangeLen - subRangeLen);
#if defined(DE_DEBUG)
T eps = T(0.001);
DE_ASSERT(aMin <= aMax && bMin <= bMax);
DE_ASSERT(de::inRange(aMin + bMin, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMin + bMax, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMax + bMin, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMax + bMax, dstMin - eps, dstMax + eps));
#endif
}
template <>
void ComputeAddRange::operator()<bool>(de::Random &, bool, bool, bool &, bool &, bool &, bool &) const
{
DE_ASSERT(false);
}
AddOp::AddOp(GeneratorState &state, ConstValueRangeAccess valueRange) : AddBase(state, Token::PLUS, valueRange)
{
}
float AddOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
if (valueRange.getType().isVoid() || valueRange.getType().isFloatOrVec() || valueRange.getType().isIntOrVec())
return AddBase::getWeight(state, valueRange);
else
return 0.0f;
}
template <typename T>
void ComputeSubRange::operator()(de::Random &random, T dstMin, T dstMax, T &aMin, T &aMax, T &bMin, T &bMax) const
{
struct GetRandom
{
int operator()(de::Random &rnd, int min, int max) const
{
return rnd.getInt(min, max);
}
float operator()(de::Random &rnd, float min, float max) const
{
return getQuantizedFloat(rnd, min, max, 0.5f);
}
};
T rangeLen = dstMax - dstMin;
T subRangeLen = GetRandom()(random, T(0), rangeLen);
T aOffset = GetRandom()(random, T(-8), T(8));
aMin = dstMin + aOffset;
aMax = aMin + subRangeLen;
bMin = aOffset - (rangeLen - subRangeLen);
bMax = aOffset;
#if defined(DE_DEBUG)
T eps = T(0.001);
DE_ASSERT(aMin <= aMax && bMin <= bMax);
DE_ASSERT(de::inRange(aMin - bMin, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMin - bMax, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMax - bMin, dstMin - eps, dstMax + eps));
DE_ASSERT(de::inRange(aMax - bMax, dstMin - eps, dstMax + eps));
#endif
}
template <>
void ComputeSubRange::operator()<bool>(de::Random &, bool, bool, bool &, bool &, bool &, bool &) const
{
DE_ASSERT(false);
}
SubOp::SubOp(GeneratorState &state, ConstValueRangeAccess valueRange) : SubBase(state, Token::MINUS, valueRange)
{
}
float SubOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
if (valueRange.getType().isVoid() || valueRange.getType().isFloatOrVec() || valueRange.getType().isIntOrVec())
return SubBase::getWeight(state, valueRange);
else
return 0.0f;
}
template <class ComputeValueRange, class EvaluateComp>
RelationalOp<ComputeValueRange, EvaluateComp>::RelationalOp(GeneratorState &state, Token::Type operatorToken,
ConstValueRangeAccess inValueRange)
: BinaryOp<7, ASSOCIATIVITY_LEFT>(operatorToken)
{
ValueRange valueRange = inValueRange;
if (valueRange.getType().isVoid())
{
valueRange = ValueRange(VariableType(VariableType::TYPE_BOOL, 1));
computeRandomValueRange(state, valueRange.asAccess());
}
// Choose type, allocate storage for execution
this->m_type = valueRange.getType();
this->m_value.setStorage(this->m_type);
// Choose random input type
VariableType::Type inBaseTypes[] = {VariableType::TYPE_FLOAT, VariableType::TYPE_INT};
VariableType::Type inBaseType =
state.getRandom().choose<VariableType::Type>(&inBaseTypes[0], &inBaseTypes[DE_LENGTH_OF_ARRAY(inBaseTypes)]);
// Initialize storage for input value ranges
this->m_rightValueRange = ValueRange(VariableType(inBaseType, 1));
this->m_leftValueRange = ValueRange(VariableType(inBaseType, 1));
// Compute range for b that satisfies requested value range
{
bool dstMin = valueRange.getMin().asBool();
bool dstMax = valueRange.getMax().asBool();
ValueRangeAccess a = this->m_leftValueRange.asAccess();
ValueRangeAccess b = this->m_rightValueRange.asAccess();
if (inBaseType == VariableType::TYPE_FLOAT)
ComputeValueRange()(state.getRandom(), dstMin, dstMax, a.getMin().asFloat(), a.getMax().asFloat(),
b.getMin().asFloat(), b.getMax().asFloat());
else if (inBaseType == VariableType::TYPE_INT)
ComputeValueRange()(state.getRandom(), dstMin, dstMax, a.getMin().asInt(), a.getMax().asInt(),
b.getMin().asInt(), b.getMax().asInt());
}
}
template <class ComputeValueRange, class EvaluateComp>
RelationalOp<ComputeValueRange, EvaluateComp>::~RelationalOp(void)
{
}
template <class ComputeValueRange, class EvaluateComp>
void RelationalOp<ComputeValueRange, EvaluateComp>::evaluate(ExecValueAccess dst, ExecConstValueAccess a,
ExecConstValueAccess b)
{
DE_ASSERT(a.getType() == b.getType());
switch (a.getType().getBaseType())
{
case VariableType::TYPE_FLOAT:
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dst.asBool(compNdx) = EvaluateComp()(a.asFloat(compNdx), b.asFloat(compNdx));
break;
case VariableType::TYPE_INT:
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
dst.asBool(compNdx) = EvaluateComp()(a.asInt(compNdx), b.asInt(compNdx));
break;
default:
DE_ASSERT(false);
}
}
template <class ComputeValueRange, class EvaluateComp>
float RelationalOp<ComputeValueRange, EvaluateComp>::getWeight(const GeneratorState &state,
ConstValueRangeAccess valueRange)
{
if (!state.getProgramParameters().useComparisonOps)
return 0.0f;
if (valueRange.getType().isVoid() ||
(valueRange.getType().getBaseType() == VariableType::TYPE_BOOL && valueRange.getType().getNumElements() == 1))
return BinaryOp<7, ASSOCIATIVITY_LEFT>::getWeight(state, valueRange);
else
return 0.0f;
}
namespace
{
template <typename T>
T getStep(void);
template <>
inline float getStep(void)
{
return 0.25f;
}
template <>
inline int getStep(void)
{
return 1;
}
} // namespace
template <typename T>
void ComputeLessThanRange::operator()(de::Random &rnd, bool dstMin, bool dstMax, T &aMin, T &aMax, T &bMin,
T &bMax) const
{
struct GetRandom
{
int operator()(de::Random &random, int min, int max) const
{
return random.getInt(min, max);
}
float operator()(de::Random &random, float min, float max) const
{
return getQuantizedFloat(random, min, max, getStep<float>());
}
};
// One random range
T rLen = GetRandom()(rnd, T(0), T(8));
T rMin = GetRandom()(rnd, T(-4), T(4));
T rMax = rMin + rLen;
if (dstMin == false && dstMax == true)
{
// Both values are possible, use same range for both inputs
aMin = rMin;
aMax = rMax;
bMin = rMin;
bMax = rMax;
}
else if (dstMin == true && dstMax == true)
{
// Compute range that is less than rMin..rMax
T aLen = GetRandom()(rnd, T(0), T(8) - rLen);
aMax = rMin - getStep<T>();
aMin = aMax - aLen;
bMin = rMin;
bMax = rMax;
}
else
{
// Compute range that is greater than or equal to rMin..rMax
T aLen = GetRandom()(rnd, T(0), T(8) - rLen);
aMin = rMax;
aMax = aMin + aLen;
bMin = rMin;
bMax = rMax;
}
}
LessThanOp::LessThanOp(GeneratorState &state, ConstValueRangeAccess valueRange)
: LessThanBase(state, Token::CMP_LT, valueRange)
{
}
float LessThanOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
return LessThanBase::getWeight(state, valueRange);
}
template <typename T>
void ComputeLessOrEqualRange::operator()(de::Random &rnd, bool dstMin, bool dstMax, T &aMin, T &aMax, T &bMin,
T &bMax) const
{
struct GetRandom
{
int operator()(de::Random &random, int min, int max) const
{
return random.getInt(min, max);
}
float operator()(de::Random &random, float min, float max) const
{
return getQuantizedFloat(random, min, max, getStep<float>());
}
};
// One random range
T rLen = GetRandom()(rnd, T(0), T(8));
T rMin = GetRandom()(rnd, T(-4), T(4));
T rMax = rMin + rLen;
if (dstMin == false && dstMax == true)
{
// Both values are possible, use same range for both inputs
aMin = rMin;
aMax = rMax;
bMin = rMin;
bMax = rMax;
}
else if (dstMin == true && dstMax == true)
{
// Compute range that is less than or equal to rMin..rMax
T aLen = GetRandom()(rnd, T(0), T(8) - rLen);
aMax = rMin;
aMin = aMax - aLen;
bMin = rMin;
bMax = rMax;
}
else
{
// Compute range that is greater than rMin..rMax
T aLen = GetRandom()(rnd, T(0), T(8) - rLen);
aMin = rMax + getStep<T>();
aMax = aMin + aLen;
bMin = rMin;
bMax = rMax;
}
}
LessOrEqualOp::LessOrEqualOp(GeneratorState &state, ConstValueRangeAccess valueRange)
: LessOrEqualBase(state, Token::CMP_LE, valueRange)
{
}
float LessOrEqualOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
return LessOrEqualBase::getWeight(state, valueRange);
}
GreaterThanOp::GreaterThanOp(GeneratorState &state, ConstValueRangeAccess valueRange)
: GreaterThanBase(state, Token::CMP_GT, valueRange)
{
}
float GreaterThanOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
return GreaterThanBase::getWeight(state, valueRange);
}
GreaterOrEqualOp::GreaterOrEqualOp(GeneratorState &state, ConstValueRangeAccess valueRange)
: GreaterOrEqualBase(state, Token::CMP_GE, valueRange)
{
}
float GreaterOrEqualOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
return GreaterOrEqualBase::getWeight(state, valueRange);
}
namespace
{
template <bool IsEqual, typename T>
void computeEqualityValueRange(de::Random &rnd, bool dstMin, bool dstMax, T &aMin, T &aMax, T &bMin, T &bMax)
{
if (dstMin == false && dstMax == true)
ComputeLessThanRange()(rnd, false, true, aMin, aMax, bMin, bMax);
else if (IsEqual && dstMin == false)
ComputeLessThanRange()(rnd, true, true, aMin, aMax, bMin, bMax);
else if (!IsEqual && dstMin == true)
ComputeLessThanRange()(rnd, true, true, aMin, aMax, bMin, bMax);
else
{
// Must have exactly same values.
struct GetRandom
{
int operator()(de::Random &random, int min, int max) const
{
return random.getInt(min, max);
}
float operator()(de::Random &random, float min, float max) const
{
return getQuantizedFloat(random, min, max, 0.5f);
}
};
T val = GetRandom()(rnd, T(-1), T(1));
aMin = val;
aMax = val;
bMin = val;
bMax = val;
}
}
template <>
void computeEqualityValueRange<true, bool>(de::Random &rnd, bool dstMin, bool dstMax, bool &aMin, bool &aMax,
bool &bMin, bool &bMax)
{
if (dstMin == false && dstMax == true)
{
aMin = false;
aMax = true;
bMin = false;
bMax = true;
}
else if (dstMin == false)
{
DE_ASSERT(dstMax == false);
bool val = rnd.getBool();
aMin = val;
aMax = val;
bMin = !val;
bMax = !val;
}
else
{
DE_ASSERT(dstMin == true && dstMax == true);
bool val = rnd.getBool();
aMin = val;
aMax = val;
bMin = val;
bMax = val;
}
}
template <>
void computeEqualityValueRange<false, bool>(de::Random &rnd, bool dstMin, bool dstMax, bool &aMin, bool &aMax,
bool &bMin, bool &bMax)
{
if (dstMin == false && dstMax == true)
computeEqualityValueRange<true>(rnd, dstMin, dstMax, aMin, aMax, bMin, bMax);
else
computeEqualityValueRange<true>(rnd, !dstMin, !dstMax, aMin, aMax, bMin, bMax);
}
} // namespace
template <bool IsEqual>
EqualityComparisonOp<IsEqual>::EqualityComparisonOp(GeneratorState &state, ConstValueRangeAccess inValueRange)
: BinaryOp<8, ASSOCIATIVITY_LEFT>(IsEqual ? Token::CMP_EQ : Token::CMP_NE)
{
ValueRange valueRange = inValueRange;
if (valueRange.getType().isVoid())
{
valueRange = ValueRange(VariableType(VariableType::TYPE_BOOL, 1));
computeRandomValueRange(state, valueRange.asAccess());
}
// Choose type, allocate storage for execution
this->m_type = valueRange.getType();
this->m_value.setStorage(this->m_type);
// Choose random input type
VariableType::Type inBaseTypes[] = {VariableType::TYPE_FLOAT, VariableType::TYPE_INT};
VariableType::Type inBaseType =
state.getRandom().choose<VariableType::Type>(&inBaseTypes[0], &inBaseTypes[DE_LENGTH_OF_ARRAY(inBaseTypes)]);
int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth();
int numElements = state.getRandom().getInt(1, availableLevels >= 3 ? 4 : 1);
// Initialize storage for input value ranges
this->m_rightValueRange = ValueRange(VariableType(inBaseType, numElements));
this->m_leftValueRange = ValueRange(VariableType(inBaseType, numElements));
// Compute range for b that satisfies requested value range
for (int elementNdx = 0; elementNdx < numElements; elementNdx++)
{
bool dstMin = valueRange.getMin().asBool();
bool dstMax = valueRange.getMax().asBool();
ValueRangeAccess a = this->m_leftValueRange.asAccess().component(elementNdx);
ValueRangeAccess b = this->m_rightValueRange.asAccess().component(elementNdx);
if (inBaseType == VariableType::TYPE_FLOAT)
computeEqualityValueRange<IsEqual>(state.getRandom(), dstMin, dstMax, a.getMin().asFloat(),
a.getMax().asFloat(), b.getMin().asFloat(), b.getMax().asFloat());
else if (inBaseType == VariableType::TYPE_INT)
computeEqualityValueRange<IsEqual>(state.getRandom(), dstMin, dstMax, a.getMin().asInt(),
a.getMax().asInt(), b.getMin().asInt(), b.getMax().asInt());
else
{
DE_ASSERT(inBaseType == VariableType::TYPE_BOOL);
computeEqualityValueRange<IsEqual>(state.getRandom(), dstMin, dstMax, a.getMin().asBool(),
a.getMax().asBool(), b.getMin().asBool(), b.getMax().asBool());
}
}
}
template <bool IsEqual>
float EqualityComparisonOp<IsEqual>::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
if (!state.getProgramParameters().useComparisonOps)
return 0.0f;
// \todo [2011-06-13 pyry] Weight down cases that would force constant inputs.
if (valueRange.getType().isVoid() ||
(valueRange.getType().getBaseType() == VariableType::TYPE_BOOL && valueRange.getType().getNumElements() == 1))
return BinaryOp<8, ASSOCIATIVITY_LEFT>::getWeight(state, valueRange);
else
return 0.0f;
}
namespace
{
template <bool IsEqual>
struct EqualityCompare
{
template <typename T>
static bool compare(T a, T b);
static bool combine(bool a, bool b);
};
template <>
template <typename T>
inline bool EqualityCompare<true>::compare(T a, T b)
{
return a == b;
}
template <>
inline bool EqualityCompare<true>::combine(bool a, bool b)
{
return a && b;
}
template <>
template <typename T>
inline bool EqualityCompare<false>::compare(T a, T b)
{
return a != b;
}
template <>
inline bool EqualityCompare<false>::combine(bool a, bool b)
{
return a || b;
}
} // namespace
template <bool IsEqual>
void EqualityComparisonOp<IsEqual>::evaluate(ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
{
DE_ASSERT(a.getType() == b.getType());
switch (a.getType().getBaseType())
{
case VariableType::TYPE_FLOAT:
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
{
bool result = IsEqual ? true : false;
for (int elemNdx = 0; elemNdx < a.getType().getNumElements(); elemNdx++)
result = EqualityCompare<IsEqual>::combine(
result, EqualityCompare<IsEqual>::compare(a.component(elemNdx).asFloat(compNdx),
b.component(elemNdx).asFloat(compNdx)));
dst.asBool(compNdx) = result;
}
break;
case VariableType::TYPE_INT:
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
{
bool result = IsEqual ? true : false;
for (int elemNdx = 0; elemNdx < a.getType().getNumElements(); elemNdx++)
result = EqualityCompare<IsEqual>::combine(
result, EqualityCompare<IsEqual>::compare(a.component(elemNdx).asInt(compNdx),
b.component(elemNdx).asInt(compNdx)));
dst.asBool(compNdx) = result;
}
break;
case VariableType::TYPE_BOOL:
for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
{
bool result = IsEqual ? true : false;
for (int elemNdx = 0; elemNdx < a.getType().getNumElements(); elemNdx++)
result = EqualityCompare<IsEqual>::combine(
result, EqualityCompare<IsEqual>::compare(a.component(elemNdx).asBool(compNdx),
b.component(elemNdx).asBool(compNdx)));
dst.asBool(compNdx) = result;
}
break;
default:
DE_ASSERT(false);
}
}
EqualOp::EqualOp(GeneratorState &state, ConstValueRangeAccess valueRange)
: EqualityComparisonOp<true>(state, valueRange)
{
}
float EqualOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
return EqualityComparisonOp<true>::getWeight(state, valueRange);
}
NotEqualOp::NotEqualOp(GeneratorState &state, ConstValueRangeAccess valueRange)
: EqualityComparisonOp<false>(state, valueRange)
{
}
float NotEqualOp::getWeight(const GeneratorState &state, ConstValueRangeAccess valueRange)
{
return EqualityComparisonOp<false>::getWeight(state, valueRange);
}
} // namespace rsg