Hash :
800e82c6
Author :
Date :
2021-08-23T11:05:23
Translator: Validate precisions
When declaring a variable, a struct field, function parameter etc,
there's a precision necessarily applied to the entity being declared.
AST Validation is added to enforce this. Intermediate nodes derive
their precision from these entities automatically.
Consistency of intermediate nodes is not validated. This is because AST
transformations replace a node with a transformed one, and that may not
have the same precision. Take the following code:
mediump float x = ...;
mediump float y = ...;
... x + y ...
and assume is transformed as such:
highp float driver_uniform;
... (x * driver_uniform) + y ...
The addition was originally done in mediump, but would seemingly need to
be done in highp after transformation. There are a number of options
here:
- Make sure that when nodes are replaced, the precision is unaffected.
This can be intrusive, requiring temp variables.
- Bubble up the new precision
- Accept the discrepancy
ANGLE opts for the last option, which actually respects the original
shader's intended precision for operations, even if some transformation
needs to temporarily evaluate an expression at a higher precision.
Bug: angleproject:4889
Bug: angleproject:6132
Change-Id: Ibcde3a230de159157783b1c6d5ef1cd63ceb4d8f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3114027
Reviewed-by: Tim Van Patten <timvp@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ClampIndirectIndices.h: Add clamp to the indirect indices used on arrays.
//
#include "compiler/translator/tree_ops/ClampIndirectIndices.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/StaticType.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
{
namespace
{
// Traverser that finds EOpIndexIndirect nodes and applies a clamp to their right-hand side
// expression.
class ClampIndirectIndicesTraverser : public TIntermTraverser
{
public:
ClampIndirectIndicesTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
: TIntermTraverser(true, false, false, symbolTable), mCompiler(compiler)
{}
bool visitBinary(Visit visit, TIntermBinary *node) override
{
ASSERT(visit == PreVisit);
// Only interested in EOpIndexIndirect nodes.
if (node->getOp() != EOpIndexIndirect)
{
return true;
}
// Apply the transformation to the left and right nodes
bool valid = ClampIndirectIndices(mCompiler, node->getLeft(), mSymbolTable);
ASSERT(valid);
valid = ClampIndirectIndices(mCompiler, node->getRight(), mSymbolTable);
ASSERT(valid);
// Generate clamp(right, 0, N), where N is the size of the array being indexed minus 1. If
// the array is runtime-sized, the length() method is called on it.
const TType &leftType = node->getLeft()->getType();
const TType &rightType = node->getRight()->getType();
// On GLSL es 100, clamp is only defined for float, so float arguments are used.
//
// However, float clamp is unconditionally emitted to workaround driver bugs with integer
// clamp on Qualcomm. http://crbug.com/1217167
//
// const bool useFloatClamp = mCompiler->getShaderVersion() == 100;
const bool useFloatClamp = true;
TIntermConstantUnion *zero = createClampValue(0, useFloatClamp);
TIntermTyped *max;
if (leftType.isUnsizedArray())
{
// Unsized arrays are an ES3.1 feature, so integer clamp should be available already.
max = new TIntermUnary(EOpArrayLength, node->getLeft(), nullptr);
max = new TIntermBinary(EOpSub, max, CreateIndexNode(1));
if (useFloatClamp)
{
TIntermSequence constructorArgs = {max};
max = TIntermAggregate::CreateConstructor(
*StaticType::GetBasic<EbtFloat, EbpHigh>(), &constructorArgs);
}
}
else if (leftType.isArray())
{
max = createClampValue(static_cast<int>(leftType.getOutermostArraySize()) - 1,
useFloatClamp);
}
else
{
ASSERT(leftType.isVector() || leftType.isMatrix());
max = createClampValue(leftType.getNominalSize() - 1, useFloatClamp);
}
TIntermTyped *index = node->getRight();
// If the index node is not an int (i.e. it's a uint), or a float (if using float clamp),
// cast it.
const TBasicType requiredBasicType = useFloatClamp ? EbtFloat : EbtInt;
if (rightType.getBasicType() != requiredBasicType)
{
const TType *clampType = useFloatClamp ? StaticType::GetBasic<EbtFloat, EbpHigh>()
: StaticType::GetBasic<EbtInt, EbpHigh>();
TIntermSequence constructorArgs = {index};
index = TIntermAggregate::CreateConstructor(*clampType, &constructorArgs);
}
// min(gl_PointSize, maxPointSize)
TIntermSequence args;
args.push_back(index);
args.push_back(zero);
args.push_back(max);
TIntermTyped *clamped =
CreateBuiltInFunctionCallNode("clamp", &args, *mSymbolTable, useFloatClamp ? 100 : 300);
// Cast back to int if float clamp was used.
if (useFloatClamp)
{
TIntermSequence constructorArgs = {clamped};
clamped = TIntermAggregate::CreateConstructor(*StaticType::GetBasic<EbtInt, EbpHigh>(),
&constructorArgs);
}
// Replace the right node (the index) with the clamped result.
queueReplacementWithParent(node, node->getRight(), clamped, OriginalNode::IS_DROPPED);
// Don't recurse as left and right nodes are already processed.
return false;
}
private:
TIntermConstantUnion *createClampValue(int value, bool useFloat)
{
if (useFloat)
{
return CreateFloatNode(static_cast<float>(value), EbpHigh);
}
return CreateIndexNode(value);
}
TCompiler *mCompiler;
};
} // anonymous namespace
bool ClampIndirectIndices(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
{
ClampIndirectIndicesTraverser traverser(compiler, symbolTable);
root->traverse(&traverser);
return traverser.updateTree(compiler, root);
}
} // namespace sh