#ifndef _RSGBUILTINFUNCTIONS_HPP #define _RSGBUILTINFUNCTIONS_HPP /*------------------------------------------------------------------------- * 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 Built-in Functions. *//*--------------------------------------------------------------------*/ #include "rsgDefs.hpp" #include "rsgExpression.hpp" #include "rsgUtils.hpp" #include "deMath.h" namespace rsg { // Template for built-in functions with form "GenType func(GenType val)". template class UnaryBuiltinVecFunc : public Expression { public: UnaryBuiltinVecFunc (GeneratorState& state, const char* function, ConstValueRangeAccess valueRange); virtual ~UnaryBuiltinVecFunc (void); Expression* createNextChild (GeneratorState& state); void tokenize (GeneratorState& state, TokenStream& str) const; void evaluate (ExecutionContext& execCtx); ExecConstValueAccess getValue (void) const { return m_value.getValue(m_inValueRange.getType()); } static float getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange); private: std::string m_function; ValueRange m_inValueRange; ExecValueStorage m_value; Expression* m_child; }; template UnaryBuiltinVecFunc::UnaryBuiltinVecFunc (GeneratorState& state, const char* function, ConstValueRangeAccess valueRange) : m_function (function) , m_inValueRange (valueRange.getType()) , m_child (DE_NULL) { DE_UNREF(state); DE_ASSERT(valueRange.getType().isFloatOrVec()); m_value.setStorage(valueRange.getType()); // Compute input value range for (int ndx = 0; ndx < m_inValueRange.getType().getNumElements(); ndx++) { ConstValueRangeAccess outRange = valueRange.component(ndx); ValueRangeAccess inRange = m_inValueRange.asAccess().component(ndx); ComputeValueRange()(outRange.getMin().asFloat(), outRange.getMax().asFloat(), inRange.getMin().asFloat(), inRange.getMax().asFloat()); } } template UnaryBuiltinVecFunc::~UnaryBuiltinVecFunc (void) { delete m_child; } template Expression* UnaryBuiltinVecFunc::createNextChild (GeneratorState& state) { if (m_child) return DE_NULL; m_child = Expression::createRandom(state, m_inValueRange.asAccess()); return m_child; } template void UnaryBuiltinVecFunc::tokenize (GeneratorState& state, TokenStream& str) const { str << Token(m_function.c_str()) << Token::LEFT_PAREN; m_child->tokenize(state, str); str << Token::RIGHT_PAREN; } template void UnaryBuiltinVecFunc::evaluate (ExecutionContext& execCtx) { m_child->evaluate(execCtx); ExecConstValueAccess srcValue = m_child->getValue(); ExecValueAccess dstValue = m_value.getValue(m_inValueRange.getType()); for (int elemNdx = 0; elemNdx < m_inValueRange.getType().getNumElements(); elemNdx++) { ExecConstValueAccess srcComp = srcValue.component(elemNdx); ExecValueAccess dstComp = dstValue.component(elemNdx); for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++) dstComp.asFloat(compNdx) = Evaluate()(srcComp.asFloat(compNdx)); } } template float UnaryBuiltinVecFunc::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange) { // \todo [2011-06-14 pyry] Void support? if (!valueRange.getType().isFloatOrVec()) return 0.0f; int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth(); if (availableLevels < getConservativeValueExprDepth(state, valueRange) + 1) return 0.0f; // Compute value range weight float combinedWeight = 1.0f; for (int elemNdx = 0; elemNdx < valueRange.getType().getNumElements(); elemNdx++) { float elemWeight = GetValueRangeWeight()(valueRange.component(elemNdx).getMin().asFloat(), valueRange.component(elemNdx).getMax().asFloat()); combinedWeight *= elemWeight; } return combinedWeight; } // Proxy template. template struct GetUnaryBuiltinVecWeight { inline float operator() (float outMin, float outMax) const { return C::getCompWeight(outMin, outMax); } }; template struct ComputeUnaryBuiltinVecRange { inline void operator() (float outMin, float outMax, float& inMin, float& inMax) const { C::computeValueRange(outMin, outMax, inMin, inMax); } }; template struct EvaluateUnaryBuiltinVec { inline float operator() (float inVal) const { return C::evaluateComp(inVal); } }; template class UnaryBuiltinVecTemplateProxy : public UnaryBuiltinVecFunc, ComputeUnaryBuiltinVecRange, EvaluateUnaryBuiltinVec > { public: UnaryBuiltinVecTemplateProxy (GeneratorState& state, const char* function, ConstValueRangeAccess valueRange) : UnaryBuiltinVecFunc, ComputeUnaryBuiltinVecRange, EvaluateUnaryBuiltinVec >(state, function, valueRange) { } }; // Template for trigonometric function group. template class UnaryTrigonometricFunc : public UnaryBuiltinVecTemplateProxy { public: UnaryTrigonometricFunc (GeneratorState& state, const char* function, ConstValueRangeAccess valueRange) : UnaryBuiltinVecTemplateProxy(state, function, valueRange) { } static inline float getCompWeight (float outMin, float outMax) { if (Scalar::min() == outMin || Scalar::max() == outMax) return 1.0f; // Infinite value range, anything goes // Transform range float inMin, inMax; if (!C::transformValueRange(outMin, outMax, inMin, inMax)) return 0.0f; // Not possible to transform value range (out of range perhaps) // Quantize if (!quantizeFloatRange(inMin, inMax)) return 0.0f; // Not possible to quantize - would cause accuracy issues if (outMin == outMax) return 1.0f; // Constant value and passed quantization // Evaluate new intersection float intersectionLen = C::evaluateComp(inMax) - C::evaluateComp(inMin); float valRangeLen = outMax - outMin; return deFloatMax(0.1f, intersectionLen/valRangeLen); } static inline void computeValueRange (float outMin, float outMax, float& inMin, float& inMax) { DE_VERIFY(C::transformValueRange(outMin, outMax, inMin, inMax)); DE_VERIFY(quantizeFloatRange(inMin, inMax)); DE_ASSERT(inMin <= inMax); } static float getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange) { if (state.getProgramParameters().trigonometricBaseWeight <= 0.0f) return 0.0f; return UnaryBuiltinVecTemplateProxy::getWeight(state, valueRange) * state.getProgramParameters().trigonometricBaseWeight; } }; class SinOp : public UnaryTrigonometricFunc { public: SinOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryTrigonometricFunc(state, "sin", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { if (outMax < -1.0f || outMin > 1.0f) return false; inMin = (outMin >= -1.0f) ? deFloatAsin(outMin) : -0.5f*DE_PI; inMax = (outMax <= +1.0f) ? deFloatAsin(outMax) : +0.5f*DE_PI; return true; } static inline float evaluateComp (float inVal) { return deFloatSin(inVal); } }; class CosOp : public UnaryTrigonometricFunc { public: CosOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryTrigonometricFunc(state, "cos", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { if (outMax < -1.0f || outMin > 1.0f) return false; inMax = (outMin >= -1.0f) ? deFloatAcos(outMin) : +DE_PI; inMin = (outMax <= +1.0f) ? deFloatAcos(outMax) : -DE_PI; return true; } static inline float evaluateComp (float inVal) { return deFloatCos(inVal); } }; class TanOp : public UnaryTrigonometricFunc { public: TanOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryTrigonometricFunc(state, "tan", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // \note Currently tan() is limited to -4..4 range. Otherwise we will run into accuracy issues const float rangeMin = -4.0f; const float rangeMax = +4.0f; if (outMax < rangeMin || outMin > rangeMax) return false; inMin = deFloatAtanOver(deFloatMax(outMin, rangeMin)); inMax = deFloatAtanOver(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatTan(inVal); } }; class AsinOp : public UnaryTrigonometricFunc { public: AsinOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryTrigonometricFunc(state, "asin", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { const float rangeMin = -DE_PI/2.0f; const float rangeMax = +DE_PI/2.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatSin(deFloatMax(outMin, rangeMin)); inMax = deFloatSin(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatAsin(inVal); } }; class AcosOp : public UnaryTrigonometricFunc { public: AcosOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryTrigonometricFunc(state, "acos", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { const float rangeMin = 0.0f; const float rangeMax = DE_PI; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMax = deFloatCos(deFloatMax(outMin, rangeMin)); inMin = deFloatCos(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatAcos(inVal); } }; class AtanOp : public UnaryTrigonometricFunc { public: AtanOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryTrigonometricFunc(state, "atan", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // \note For accuracy reasons output range is limited to -1..1 const float rangeMin = -1.0f; const float rangeMax = +1.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatTan(deFloatMax(outMin, rangeMin)); inMax = deFloatTan(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatAtanOver(inVal); } }; // Template for exponential function group. // \todo [2011-07-07 pyry] Shares most of the code with Trigonometric variant.. template class UnaryExponentialFunc : public UnaryBuiltinVecTemplateProxy { public: UnaryExponentialFunc (GeneratorState& state, const char* function, ConstValueRangeAccess valueRange) : UnaryBuiltinVecTemplateProxy(state, function, valueRange) { } static inline float getCompWeight (float outMin, float outMax) { if (Scalar::min() == outMin || Scalar::max() == outMax) return 1.0f; // Infinite value range, anything goes // Transform range float inMin, inMax; if (!C::transformValueRange(outMin, outMax, inMin, inMax)) return 0.0f; // Not possible to transform value range (out of range perhaps) // Quantize if (!quantizeFloatRange(inMin, inMax)) return 0.0f; // Not possible to quantize - would cause accuracy issues if (outMin == outMax) return 1.0f; // Constant value and passed quantization // Evaluate new intersection float intersectionLen = C::evaluateComp(inMax) - C::evaluateComp(inMin); float valRangeLen = outMax - outMin; return deFloatMax(0.1f, intersectionLen/valRangeLen); } static inline void computeValueRange (float outMin, float outMax, float& inMin, float& inMax) { DE_VERIFY(C::transformValueRange(outMin, outMax, inMin, inMax)); DE_VERIFY(quantizeFloatRange(inMin, inMax)); DE_ASSERT(inMin <= inMax); } static float getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange) { if (state.getProgramParameters().exponentialBaseWeight <= 0.0f) return 0.0f; return UnaryBuiltinVecTemplateProxy::getWeight(state, valueRange) * state.getProgramParameters().exponentialBaseWeight; } }; class ExpOp : public UnaryExponentialFunc { public: ExpOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryExponentialFunc(state, "exp", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // Limited due to accuracy reasons, should be 0..+inf const float rangeMin = 0.1f; const float rangeMax = 10.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatLog(deFloatMax(outMin, rangeMin)); inMax = deFloatLog(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatExp(inVal); } }; class LogOp : public UnaryExponentialFunc { public: LogOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryExponentialFunc(state, "log", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // Limited due to accuracy reasons, should be -inf..+inf const float rangeMin = 0.1f; const float rangeMax = 6.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatExp(deFloatMax(outMin, rangeMin)); inMax = deFloatExp(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatLog(inVal); } }; class Exp2Op : public UnaryExponentialFunc { public: Exp2Op (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryExponentialFunc(state, "exp2", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // Limited due to accuracy reasons, should be 0..+inf const float rangeMin = 0.1f; const float rangeMax = 10.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatLog2(deFloatMax(outMin, rangeMin)); inMax = deFloatLog2(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatExp2(inVal); } }; class Log2Op : public UnaryExponentialFunc { public: Log2Op (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryExponentialFunc(state, "log2", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // Limited due to accuracy reasons, should be -inf..+inf const float rangeMin = 0.1f; const float rangeMax = 6.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatExp2(deFloatMax(outMin, rangeMin)); inMax = deFloatExp2(deFloatMin(outMax, rangeMax)); return true; } static inline float evaluateComp (float inVal) { return deFloatLog2(inVal); } }; class SqrtOp : public UnaryExponentialFunc { public: SqrtOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryExponentialFunc(state, "sqrt", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // Limited due to accuracy reasons, should be 0..+inf const float rangeMin = 0.0f; const float rangeMax = 4.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMin = deFloatMax(outMin, rangeMin); inMax = deFloatMin(outMax, rangeMax); inMin *= inMin; inMax *= inMax; return true; } static inline float evaluateComp (float inVal) { return deFloatSqrt(inVal); } }; class InvSqrtOp : public UnaryExponentialFunc { public: InvSqrtOp (GeneratorState& state, ConstValueRangeAccess valueRange) : UnaryExponentialFunc(state, "inversesqrt", valueRange) { } static inline bool transformValueRange (float outMin, float outMax, float& inMin, float& inMax) { // Limited due to accuracy reasons const float rangeMin = 0.4f; const float rangeMax = 3.0f; if (outMax < rangeMin || outMin > rangeMax) return false; // Out of range inMax = 1.0f/deFloatMax(outMin, rangeMin); inMin = 1.0f/deFloatMin(outMax, rangeMax); inMin *= inMin; inMax *= inMax; return true; } static inline float evaluateComp (float inVal) { return 1.0f/deFloatSqrt(inVal); } }; } // rsg #endif // _RSGBUILTINFUNCTIONS_HPP