Source code
Revision control
Copy as Markdown
Other Tools
//
// Copyright 2019 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.
//
// Implementation of dFdy viewport transformation.
// See header for more info.
#include "compiler/translator/tree_ops/RewriteDfdy.h"
#include "common/angleutils.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/TranslatorVulkan.h"
#include "compiler/translator/tree_util/DriverUniform.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/tree_util/SpecializationConstant.h"
namespace sh
{
namespace
{
class Traverser : public TIntermTraverser
{
public:
Traverser(TSymbolTable *symbolTable, SpecConst *specConst, const DriverUniform *driverUniforms);
private:
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
SpecConst *mSpecConst = nullptr;
const DriverUniform *mDriverUniforms = nullptr;
};
Traverser::Traverser(TSymbolTable *symbolTable,
SpecConst *specConst,
const DriverUniform *driverUniforms)
: TIntermTraverser(true, false, false, symbolTable),
mSpecConst(specConst),
mDriverUniforms(driverUniforms)
{}
bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
// Decide if the node represents a call to dFdx() or dFdy()
if (node->getOp() != EOpDFdx && node->getOp() != EOpDFdy)
{
return true;
}
const bool isDFdx = node->getOp() == EOpDFdx;
// Two transformations are done on dFdx and dFdy:
//
// - If pre-rotation is applied, dFdx and dFdy may need to swap their axis based on the degree
// of rotation. dFdx becomes dFdy if rotation is 90 or 270 degrees. Similarly, dFdy becomes
// dFdx.
// - The result is potentially negated. This could be due to viewport y-flip or pre-rotation.
//
// Accordingly, there are two variables controlling the above transformations:
//
// - Rotation: A vec2 that is either (0, 1) or (1, 0). dFdx and dFdy are replaced with:
//
// dFdx * Rotation.x + dFdy * Rotation.y
//
// - Scale: A vec2 with -1 or 1 for either x or y components. The previous result is multiplied
// by this.
//
// Together, the above operations account for the combinations of 4 possible rotations and
// y-flip.
// Get the results of dFdx(operand) and dFdy(operand), and multiply them by the swizzles
TIntermTyped *operand = node->getChildNode(0)->getAsTyped();
TIntermTyped *dFdx = CreateBuiltInUnaryFunctionCallNode("dFdx", operand, *mSymbolTable, 300);
TIntermTyped *dFdy =
CreateBuiltInUnaryFunctionCallNode("dFdy", operand->deepCopy(), *mSymbolTable, 300);
// Get rotation multiplier
TIntermTyped *swapXY = mSpecConst->getSwapXY();
if (swapXY == nullptr)
{
swapXY = mDriverUniforms->getSwapXY();
}
TIntermTyped *swapXMultiplier = MakeSwapXMultiplier(swapXY);
TIntermTyped *swapYMultiplier = MakeSwapYMultiplier(swapXY->deepCopy());
// Get flip multiplier
TIntermTyped *flipXY = mDriverUniforms->getFlipXY(mSymbolTable, DriverUniformFlip::Fragment);
// Multiply the flip and rotation multipliers
TIntermTyped *xMultiplier =
new TIntermBinary(EOpMul, isDFdx ? swapXMultiplier : swapYMultiplier,
(new TIntermSwizzle(flipXY->deepCopy(), {0}))->fold(nullptr));
TIntermTyped *yMultiplier =
new TIntermBinary(EOpMul, isDFdx ? swapYMultiplier : swapXMultiplier,
(new TIntermSwizzle(flipXY->deepCopy(), {1}))->fold(nullptr));
const TOperator mulOp = dFdx->getType().isVector() ? EOpVectorTimesScalar : EOpMul;
TIntermTyped *rotatedFlippedDfdx = new TIntermBinary(mulOp, dFdx, xMultiplier);
TIntermTyped *rotatedFlippedDfdy = new TIntermBinary(mulOp, dFdy, yMultiplier);
// Sum them together into the result
TIntermBinary *rotatedFlippedResult =
new TIntermBinary(EOpAdd, rotatedFlippedDfdx, rotatedFlippedDfdy);
// Replace the old dFdx() or dFdy() node with the new node that contains the corrected value
//
// Note the following bugs (anglebug.com/7346):
//
// - Side effects of operand are duplicated with the above
// - If the direct child of this node is itself dFdx/y, its queueReplacement will not be
// effective as the parent is also replaced.
queueReplacement(rotatedFlippedResult, OriginalNode::IS_DROPPED);
return true;
}
} // anonymous namespace
bool RewriteDfdy(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
int shaderVersion,
SpecConst *specConst,
const DriverUniform *driverUniforms)
{
// dFdx/dFdy is only valid in GLSL 3.0 and later.
if (shaderVersion < 300)
{
return true;
}
Traverser traverser(symbolTable, specConst, driverUniforms);
root->traverse(&traverser);
return traverser.updateTree(compiler, root);
}
} // namespace sh