bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node) { switch (node->getOp()) { case EOpNegative: case EOpVectorLogicalNot: case EOpLogicalNot: break; case EOpPostIncrement: case EOpPostDecrement: case EOpPreIncrement: case EOpPreDecrement: if (visit == PreVisit) mInLValue = true; else if (visit == PostVisit) mInLValue = false; break; default: if (canRoundFloat(node->getType()) && visit == PreVisit) { TIntermNode *parent = getParentNode(); TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); } break; } return true; }
bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) { if (visit != PreVisit) return true; switch (node->getOp()) { case EOpCallInternalRawFunction: case EOpCallFunctionInAST: // User-defined function return values are not rounded. The calculations that produced // the value inside the function definition should have been rounded. break; case EOpConstruct: if (node->getBasicType() == EbtStruct) { break; } default: TIntermNode *parent = getParentNode(); if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) && !ParentConstructorTakesCareOfRounding(parent, node)) { TIntermNode *replacement = createRoundingFunctionCallNode(node); queueReplacement(replacement, OriginalNode::BECOMES_CHILD); } break; } return true; }
void EmulatePrecision::visitSymbol(TIntermSymbol *node) { if (canRoundFloat(node->getType()) && !mDeclaringVariables && !mInLValue && !mInFunctionCallOutParameter) { TIntermNode *parent = getParentNode(); TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); } }
void EmulatePrecision::visitSymbol(TIntermSymbol *node) { TIntermNode *parent = getParentNode(); if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) && !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables && !isLValueRequiredHere()) { TIntermNode *replacement = createRoundingFunctionCallNode(node); queueReplacement(replacement, OriginalNode::BECOMES_CHILD); } }
bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node) { switch (node->getOp()) { case EOpNegative: case EOpLogicalNot: case EOpPostIncrement: case EOpPostDecrement: case EOpPreIncrement: case EOpPreDecrement: case EOpLogicalNotComponentWise: break; default: if (canRoundFloat(node->getType()) && visit == PreVisit) { TIntermNode *replacement = createRoundingFunctionCallNode(node); queueReplacement(replacement, OriginalNode::BECOMES_CHILD); } break; } return true; }
bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node) { bool visitChildren = true; TOperator op = node->getOp(); // RHS of initialize is not being declared. if (op == EOpInitialize && visit == InVisit) mDeclaringVariables = false; if ((op == EOpIndexDirectStruct) && visit == InVisit) visitChildren = false; if (visit != PreVisit) return visitChildren; const TType &type = node->getType(); bool roundFloat = canRoundFloat(type); if (roundFloat) { switch (op) { // Math operators that can result in a float may need to apply rounding to the return // value. Note that in the case of assignment, the rounding is applied to its return // value here, not the value being assigned. case EOpAssign: case EOpAdd: case EOpSub: case EOpMul: case EOpDiv: case EOpVectorTimesScalar: case EOpVectorTimesMatrix: case EOpMatrixTimesVector: case EOpMatrixTimesScalar: case EOpMatrixTimesMatrix: { TIntermNode *parent = getParentNode(); if (!ParentUsesResult(parent, node) || ParentConstructorTakesCareOfRounding(parent, node)) { break; } TIntermNode *replacement = createRoundingFunctionCallNode(node); queueReplacement(replacement, OriginalNode::BECOMES_CHILD); break; } // Compound assignment cases need to replace the operator with a function call. case EOpAddAssign: { mEmulateCompoundAdd.insert( TypePair(type.getBuiltInTypeNameString(), node->getRight()->getType().getBuiltInTypeNameString())); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode( node->getLeft(), node->getRight(), "add"); queueReplacement(replacement, OriginalNode::IS_DROPPED); break; } case EOpSubAssign: { mEmulateCompoundSub.insert( TypePair(type.getBuiltInTypeNameString(), node->getRight()->getType().getBuiltInTypeNameString())); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode( node->getLeft(), node->getRight(), "sub"); queueReplacement(replacement, OriginalNode::IS_DROPPED); break; } case EOpMulAssign: case EOpVectorTimesMatrixAssign: case EOpVectorTimesScalarAssign: case EOpMatrixTimesScalarAssign: case EOpMatrixTimesMatrixAssign: { mEmulateCompoundMul.insert( TypePair(type.getBuiltInTypeNameString(), node->getRight()->getType().getBuiltInTypeNameString())); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode( node->getLeft(), node->getRight(), "mul"); queueReplacement(replacement, OriginalNode::IS_DROPPED); break; } case EOpDivAssign: { mEmulateCompoundDiv.insert( TypePair(type.getBuiltInTypeNameString(), node->getRight()->getType().getBuiltInTypeNameString())); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode( node->getLeft(), node->getRight(), "div"); queueReplacement(replacement, OriginalNode::IS_DROPPED); break; } default: // The rest of the binary operations should not need precision emulation. break; } } return visitChildren; }
bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) { bool visitChildren = true; switch (node->getOp()) { case EOpSequence: case EOpConstructStruct: // No special handling break; case EOpFunction: if (visit == PreVisit) { const TIntermSequence &sequence = *(node->getSequence()); TIntermSequence::const_iterator seqIter = sequence.begin(); TIntermAggregate *params = (*seqIter)->getAsAggregate(); ASSERT(params != NULL); ASSERT(params->getOp() == EOpParameters); mFunctionMap[node->getName()] = params->getSequence(); } break; case EOpPrototype: if (visit == PreVisit) mFunctionMap[node->getName()] = node->getSequence(); visitChildren = false; break; case EOpParameters: visitChildren = false; break; case EOpInvariantDeclaration: visitChildren = false; break; case EOpDeclaration: // Variable declaration. if (visit == PreVisit) { mDeclaringVariables = true; } else if (visit == InVisit) { mDeclaringVariables = true; } else { mDeclaringVariables = false; } break; case EOpFunctionCall: { // Function call. bool inFunctionMap = (mFunctionMap.find(node->getName()) != mFunctionMap.end()); if (visit == PreVisit) { // User-defined function return values are not rounded, this relies on that // calculations producing the value were rounded. TIntermNode *parent = getParentNode(); if (canRoundFloat(node->getType()) && !inFunctionMap && parentUsesResult(parent, node)) { TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); } if (inFunctionMap) { mSeqIterStack.push_back(mFunctionMap[node->getName()]->begin()); if (mSeqIterStack.back() != mFunctionMap[node->getName()]->end()) { TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier(); mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut); } } else { // The function is not user-defined - it is likely built-in texture function. // Assume that those do not have out parameters. mInFunctionCallOutParameter = false; } } else if (visit == InVisit) { if (inFunctionMap) { ++mSeqIterStack.back(); TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier(); mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut); } } else { if (inFunctionMap) { mSeqIterStack.pop_back(); mInFunctionCallOutParameter = false; } } break; } default: TIntermNode *parent = getParentNode(); if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node)) { TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); } break; } return visitChildren; }
bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node) { bool visitChildren = true; if (node->isAssignment()) { if (visit == PreVisit) mInLValue = true; else if (visit == InVisit) mInLValue = false; } TOperator op = node->getOp(); // RHS of initialize is not being declared. if (op == EOpInitialize && visit == InVisit) mDeclaringVariables = false; if ((op == EOpIndexDirectStruct || op == EOpVectorSwizzle) && visit == InVisit) visitChildren = false; if (visit != PreVisit) return visitChildren; const TType& type = node->getType(); bool roundFloat = canRoundFloat(type); if (roundFloat) { switch (op) { // Math operators that can result in a float may need to apply rounding to the return // value. Note that in the case of assignment, the rounding is applied to its return // value here, not the value being assigned. case EOpAssign: case EOpAdd: case EOpSub: case EOpMul: case EOpDiv: case EOpVectorTimesScalar: case EOpVectorTimesMatrix: case EOpMatrixTimesVector: case EOpMatrixTimesScalar: case EOpMatrixTimesMatrix: { TIntermNode *parent = getParentNode(); if (!parentUsesResult(parent, node)) { break; } TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); break; } // Compound assignment cases need to replace the operator with a function call. case EOpAddAssign: { mEmulateCompoundAdd.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType()))); TIntermNode *parent = getParentNode(); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "add"); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false)); break; } case EOpSubAssign: { mEmulateCompoundSub.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType()))); TIntermNode *parent = getParentNode(); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "sub"); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false)); break; } case EOpMulAssign: case EOpVectorTimesMatrixAssign: case EOpVectorTimesScalarAssign: case EOpMatrixTimesScalarAssign: case EOpMatrixTimesMatrixAssign: { mEmulateCompoundMul.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType()))); TIntermNode *parent = getParentNode(); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "mul"); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false)); break; } case EOpDivAssign: { mEmulateCompoundDiv.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType()))); TIntermNode *parent = getParentNode(); TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "div"); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false)); break; } default: // The rest of the binary operations should not need precision emulation. break; } } return visitChildren; }