Commit 18b9deb4 by Olli Etuaho

Support writing initializers using HLSL literal syntax

Instead of using constructor functions to initialize variables, it is better to use literal initializer syntax provided by HLSL when it is possible. This way shader complexity is reduced and constant array initialization doesn't have to go through as many AST transformations. Before this patch, vec4 initialization would result in the following kind of HLSL: float4 f = float4(1.0, 2.0, 3.0, 4.0); After this patch, it will be: float4 f = {1.0, 2.0, 3.0, 4.0}; Before this patch, vec2 array initialization would result in the following kind of HLSL: float2 f[2] = {0, 0, 0, 0}; angle_construct_into_2_float2(f, float2(1.0, 2.0), float2(3.0, 4.0)); After this patch, it will be: float2 f[2] = {1.0, 2.0, 3.0, 4.0}; BUG=angleproject:1094 BUG=541551 TEST=WebGL conformance tests Change-Id: I9816a8d95a2cba3964922f6b561862d478da6145 Reviewed-on: https://chromium-review.googlesource.com/311160Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 26d84357
...@@ -35,6 +35,45 @@ bool IsSequence(TIntermNode *node) ...@@ -35,6 +35,45 @@ bool IsSequence(TIntermNode *node)
return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence;
} }
void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
{
ASSERT(constUnion != nullptr);
switch (constUnion->getType())
{
case EbtFloat:
out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst()));
break;
case EbtInt:
out << constUnion->getIConst();
break;
case EbtUInt:
out << constUnion->getUConst();
break;
case EbtBool:
out << constUnion->getBConst();
break;
default:
UNREACHABLE();
}
}
const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
const TConstantUnion *const constUnion,
const size_t size)
{
const TConstantUnion *constUnionIterated = constUnion;
for (size_t i = 0; i < size; i++, constUnionIterated++)
{
WriteSingleConstant(out, constUnionIterated);
if (i != size - 1)
{
out << ", ";
}
}
return constUnionIterated;
}
} // namespace } // namespace
namespace sh namespace sh
...@@ -1514,6 +1553,10 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) ...@@ -1514,6 +1553,10 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
// Skip initializing the rest of the expression // Skip initializing the rest of the expression
return false; return false;
} }
else if (writeConstantInitialization(out, symbolNode, expression))
{
return false;
}
} }
else if (visit == InVisit) else if (visit == InVisit)
{ {
...@@ -2913,10 +2956,13 @@ void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *n ...@@ -2913,10 +2956,13 @@ void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *n
} }
} }
const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TConstantUnion *constUnion) const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type,
const TConstantUnion *const constUnion)
{ {
TInfoSinkBase &out = getInfoSink(); TInfoSinkBase &out = getInfoSink();
const TConstantUnion *constUnionIterated = constUnion;
const TStructure* structure = type.getStruct(); const TStructure* structure = type.getStruct();
if (structure) if (structure)
{ {
...@@ -2927,7 +2973,7 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TC ...@@ -2927,7 +2973,7 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TC
for (size_t i = 0; i < fields.size(); i++) for (size_t i = 0; i < fields.size(); i++)
{ {
const TType *fieldType = fields[i]->type(); const TType *fieldType = fields[i]->type();
constUnion = writeConstantUnion(*fieldType, constUnion); constUnionIterated = writeConstantUnion(*fieldType, constUnionIterated);
if (i != fields.size() - 1) if (i != fields.size() - 1)
{ {
...@@ -2946,31 +2992,14 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TC ...@@ -2946,31 +2992,14 @@ const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TC
{ {
out << TypeString(type) << "("; out << TypeString(type) << "(";
} }
constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size);
for (size_t i = 0; i < size; i++, constUnion++)
{
switch (constUnion->getType())
{
case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break;
case EbtInt: out << constUnion->getIConst(); break;
case EbtUInt: out << constUnion->getUConst(); break;
case EbtBool: out << constUnion->getBConst(); break;
default: UNREACHABLE();
}
if (i != size - 1)
{
out << ", ";
}
}
if (writeType) if (writeType)
{ {
out << ")"; out << ")";
} }
} }
return constUnion; return constUnionIterated;
} }
void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr) void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr)
...@@ -3000,6 +3029,68 @@ bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *s ...@@ -3000,6 +3029,68 @@ bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *s
return false; return false;
} }
bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression)
{
// We support writing constant unions and constructors that only take constant unions as
// parameters as HLSL literals.
if (expression->getAsConstantUnion())
{
return true;
}
if (expression->getQualifier() != EvqConst || !expression->getAsAggregate() ||
!expression->getAsAggregate()->isConstructor())
{
return false;
}
TIntermAggregate *constructor = expression->getAsAggregate();
for (TIntermNode *&node : *constructor->getSequence())
{
if (!node->getAsConstantUnion())
return false;
}
return true;
}
bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
TIntermSymbol *symbolNode,
TIntermTyped *expression)
{
if (canWriteAsHLSLLiteral(expression))
{
symbolNode->traverse(this);
if (expression->getType().isArray())
{
out << "[" << expression->getType().getArraySize() << "]";
}
out << " = {";
if (expression->getAsConstantUnion())
{
TIntermConstantUnion *nodeConst = expression->getAsConstantUnion();
const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
}
else
{
TIntermAggregate *constructor = expression->getAsAggregate();
ASSERT(constructor != nullptr);
for (TIntermNode *&node : *constructor->getSequence())
{
TIntermConstantUnion *nodeConst = node->getAsConstantUnion();
ASSERT(nodeConst);
const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
if (node != constructor->getSequence()->back())
{
out << ", ";
}
}
}
out << "}";
return true;
}
return false;
}
void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out)
{ {
out << "#define ANGLE_USES_DEFERRED_INIT\n" out << "#define ANGLE_USES_DEFERRED_INIT\n"
......
...@@ -47,6 +47,8 @@ class OutputHLSL : public TIntermTraverser ...@@ -47,6 +47,8 @@ class OutputHLSL : public TIntermTraverser
TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); } TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); }
static bool canWriteAsHLSLLiteral(TIntermTyped *expression);
protected: protected:
void header(const BuiltInFunctionEmulator *builtInFunctionEmulator); void header(const BuiltInFunctionEmulator *builtInFunctionEmulator);
...@@ -84,6 +86,11 @@ class OutputHLSL : public TIntermTraverser ...@@ -84,6 +86,11 @@ class OutputHLSL : public TIntermTraverser
// Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting) // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting)
bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression); bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression);
// Returns true if variable initializer could be written using literal {} notation.
bool writeConstantInitialization(TInfoSinkBase &out,
TIntermSymbol *symbolNode,
TIntermTyped *expression);
void writeDeferredGlobalInitializers(TInfoSinkBase &out); void writeDeferredGlobalInitializers(TInfoSinkBase &out);
void writeSelection(TIntermSelection *node); void writeSelection(TIntermSelection *node);
......
...@@ -10,13 +10,15 @@ ...@@ -10,13 +10,15 @@
// type[n] a; // type[n] a;
// a = initializer; // a = initializer;
// //
// Note that if the array is declared as const, the initialization is still split, making the AST // Note that if the array is declared as const, the initialization may still be split, making the
// technically invalid. Because of that this transformation should only be used when subsequent // AST technically invalid. Because of that this transformation should only be used when subsequent
// stages don't care about const qualifiers. // stages don't care about const qualifiers. However, the initialization will not be split if the
// initializer can be written as a HLSL literal.
#include "compiler/translator/SeparateArrayInitialization.h" #include "compiler/translator/SeparateArrayInitialization.h"
#include "compiler/translator/IntermNode.h" #include "compiler/translator/IntermNode.h"
#include "compiler/translator/OutputHLSL.h"
namespace namespace
{ {
...@@ -51,7 +53,7 @@ bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node) ...@@ -51,7 +53,7 @@ bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node)
if (initNode != nullptr && initNode->getOp() == EOpInitialize) if (initNode != nullptr && initNode->getOp() == EOpInitialize)
{ {
TIntermTyped *initializer = initNode->getRight(); TIntermTyped *initializer = initNode->getRight();
if (initializer->isArray()) if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer))
{ {
// We rely on that array declarations have been isolated to single declarations. // We rely on that array declarations have been isolated to single declarations.
ASSERT(sequence->size() == 1); ASSERT(sequence->size() == 1);
......
...@@ -10,9 +10,10 @@ ...@@ -10,9 +10,10 @@
// type[n] a; // type[n] a;
// a = initializer; // a = initializer;
// //
// Note that if the array is declared as const, the initialization is still split, making the AST // Note that if the array is declared as const, the initialization may still be split, making the
// technically invalid. Because of that this transformation should only be used when subsequent // AST technically invalid. Because of that this transformation should only be used when subsequent
// stages don't care about const qualifiers. // stages don't care about const qualifiers. However, the initialization will not be split if the
// initializer can be written as a HLSL literal.
#ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ #ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
#define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ #define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment