blob: 581dda210041286dcf6151c682ea0153b373ff16 [file]
//
// Copyright 2002 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.
//
#include "compiler/translator/hlsl/TranslatorHLSL.h"
#include "compiler/translator/hlsl/OutputHLSL.h"
#include "compiler/translator/tree_ops/AddDefaultReturnStatements.h"
#include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h"
#include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
#include "compiler/translator/tree_ops/RewriteStructSamplers.h"
#include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h"
#include "compiler/translator/tree_ops/SimplifyLoopConditions.h"
#include "compiler/translator/tree_ops/SplitSequenceOperator.h"
#include "compiler/translator/tree_ops/UseGeneratedNamesForAnonymousStructs.h"
#include "compiler/translator/tree_ops/hlsl/ArrayReturnValueToOutParameter.h"
#include "compiler/translator/tree_ops/hlsl/BreakVariableAliasingInInnerLoops.h"
#include "compiler/translator/tree_ops/hlsl/ExpandIntegerPowExpressions.h"
#include "compiler/translator/tree_ops/hlsl/RecordUniformBlocksWithLargeArrayMember.h"
#include "compiler/translator/tree_ops/hlsl/RewriteElseBlocks.h"
#include "compiler/translator/tree_ops/hlsl/RewriteUnaryMinusOperatorInt.h"
#include "compiler/translator/tree_ops/hlsl/SeparateArrayConstructorStatements.h"
#include "compiler/translator/tree_ops/hlsl/SeparateArrayInitialization.h"
#include "compiler/translator/tree_ops/hlsl/SeparateExpressionsReturningArrays.h"
#include "compiler/translator/tree_ops/hlsl/UnfoldShortCircuitToIf.h"
#include "compiler/translator/tree_ops/hlsl/WrapSwitchStatementsInBlocks.h"
#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
namespace sh
{
namespace
{
bool CollectStructSamplerPaths(const ShaderVariable &variable,
const std::string &path,
std::vector<std::string> *pathsOut)
{
if (gl::IsSamplerType(variable.type))
{
if (pathsOut)
{
pathsOut->push_back(path);
}
return true;
}
bool containsSampler = false;
for (const ShaderVariable &field : variable.fields)
{
const std::string fieldPath = pathsOut ? path + "." + field.name : std::string();
if (CollectStructSamplerPaths(field, fieldPath, pathsOut))
{
containsSampler = true;
}
}
return containsSampler;
}
bool BuildExtractedSamplerNameMap(TIntermBlock *root,
const std::vector<std::string> &paths,
ExtractedSamplerNameMap *namesOut)
{
// RewriteStructSamplers declares extracted samplers in the same DFS order as the reflected
// sampler fields.
std::map<std::string, const TVariable *> variables;
for (TIntermNode *node : *root->getSequence())
{
TIntermDeclaration *declaration = node->getAsDeclarationNode();
if (!declaration)
{
continue;
}
for (TIntermNode *declarator : *declaration->getSequence())
{
TIntermSymbol *symbol = declarator->getAsSymbolNode();
if (symbol && symbol->variable().symbolType() == SymbolType::AngleInternal &&
symbol->getQualifier() == EvqUniform && symbol->getType().isSampler() &&
symbol->getName().beginsWith(kExtractedSamplerNamePrefix))
{
const ImmutableString &name = symbol->getName();
if (!variables.emplace(std::string(name.data(), name.length()), &symbol->variable())
.second)
{
return false;
}
}
}
}
if (variables.size() != paths.size())
{
return false;
}
for (size_t index = 0; index < variables.size(); ++index)
{
const std::string name =
std::string(kExtractedSamplerNamePrefix) + '_' + std::to_string(index);
auto variable = variables.find(name);
if (variable == variables.end())
{
return false;
}
if (!namesOut->emplace(variable->second, paths[index]).second)
{
return false;
}
}
return true;
}
} // anonymous namespace
TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
: TCompiler(type, spec, output)
{}
bool TranslatorHLSL::translate(TIntermBlock *root,
const ShCompileOptions &compileOptions,
PerformanceDiagnostics *perfDiagnostics)
{
// A few transformations leave the tree in an inconsistent state. For example, when unfolding
// the short-circuit in the following function:
//
// mediump float f(float a) { return a > 0 ? 0.0 : 1.0; }
//
// a temp variable is created to hold the result of the expression. Currently the precision of
// the return value of the function is not propagated to its return expressions. Additionally,
// an expression such as
//
// cond ? gl_<SomeConstant>.x : gl_<SomeConstant>.y
//
// does not have a precision as the built-in does not specify a precision.
//
// Precision is not applicable to HLSL so fixing these issues are deferred.
mValidateASTOptions.validatePrecision = false;
const ShBuiltInResources &resources = getResources();
int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
int maxDualSourceDrawBuffers =
resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0;
if (!compileOptions.useIR)
{
if (!sh::AddDefaultReturnStatements(this, root))
{
return false;
}
}
// The HLSL generator prefers every struct to have a name, and is not bound by GLSL's
// requirement that anonymous structs match their (lack of) name between shader stages.
if (!UseGeneratedNamesForAnonymousStructs(this, root))
{
return false;
}
// Note that SimplifyLoopConditions needs to be run before any other AST transformations that
// may need to generate new statements from loop conditions or loop expressions.
// Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl().
if (!SimplifyLoopConditions(
this, root,
IntermNodePatternMatcher::kExpressionReturningArray |
IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
&getSymbolTable()))
{
return false;
}
if (!SplitSequenceOperator(
this, root,
IntermNodePatternMatcher::kExpressionReturningArray |
IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
&getSymbolTable()))
{
return false;
}
// Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
if (!UnfoldShortCircuitToIf(this, root, &getSymbolTable()))
{
return false;
}
if (!SeparateArrayConstructorStatements(this, root))
{
return false;
}
if (!SeparateExpressionsReturningArrays(this, root, &getSymbolTable()))
{
return false;
}
// Note that SeparateDeclarations needs to be run before SeparateArrayInitialization.
if (!SeparateArrayInitialization(this, root))
{
return false;
}
// HLSL doesn't support arrays as return values, we'll need to make functions that have an array
// as a return value to use an out parameter to transfer the array data instead.
if (!ArrayReturnValueToOutParameter(this, root, &getSymbolTable()))
{
return false;
}
if (!shouldRunLoopAndIndexingValidation())
{
// HLSL doesn't support dynamic indexing of vectors and matrices.
if (!RemoveDynamicIndexingOfNonSSBOVectorOrMatrix(this, root, &getSymbolTable(),
perfDiagnostics))
{
return false;
}
}
// Work around an HLSL compiler frontend aliasing optimization bug.
// TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
// in the next release of d3dcompiler.dll, it would be nice to detect the DLL
// version and only apply the workaround if it is too old.
if (!sh::BreakVariableAliasingInInnerLoops(this, root))
{
return false;
}
// WrapSwitchStatementsInBlocks should be called after any AST transformations that might
// introduce variable declarations inside the main scope of any switch statement. It cannot
// result in no-op cases at the end of switch statements, because unreferenced variables
// have already been pruned.
if (!WrapSwitchStatementsInBlocks(this, root))
{
return false;
}
if (compileOptions.expandSelectHLSLIntegerPowExpressions)
{
if (!sh::ExpandIntegerPowExpressions(this, root, &getSymbolTable()))
{
return false;
}
}
if (compileOptions.rewriteTexelFetchOffsetToTexelFetch)
{
if (!sh::RewriteTexelFetchOffset(this, root, getSymbolTable(), getShaderVersion()))
{
return false;
}
}
if (compileOptions.rewriteIntegerUnaryMinusOperator && getShaderType() == GL_VERTEX_SHADER)
{
if (!sh::RewriteUnaryMinusOperatorInt(this, root))
{
return false;
}
}
ExtractedSamplerNameMap extractedSamplerNames;
std::vector<std::string> extractedSamplerPaths;
bool hasStructSamplers = false;
for (const ShaderVariable &uniform : getUniforms())
{
std::vector<std::string> *pathsOut = uniform.active ? &extractedSamplerPaths : nullptr;
if (uniform.isStruct() && CollectStructSamplerPaths(uniform, uniform.name, pathsOut))
{
hasStructSamplers = true;
}
}
if (hasStructSamplers)
{
if (!compileOptions.useIR)
{
UnsupportedFunctionArgsBitSet args{UnsupportedFunctionArgs::StructContainingSamplers};
if (!MonomorphizeUnsupportedFunctions(this, root, &getSymbolTable(), args))
{
return false;
}
if (!RewriteStructSamplers(this, root, &getSymbolTable()))
{
return false;
}
}
if (!BuildExtractedSamplerNameMap(root, extractedSamplerPaths, &extractedSamplerNames))
{
return false;
}
}
mUniformBlockOptimizedMap.clear();
mSlowCompilingUniformBlockSet.clear();
// In order to get the exact maximum of slots are available for shader resources, which would
// been bound with StructuredBuffer, we only translate uniform block with a large array member
// into StructuredBuffer when shader version is 300.
if (getShaderVersion() == 300 && compileOptions.allowTranslateUniformBlockToStructuredBuffer)
{
if (!sh::RecordUniformBlocksWithLargeArrayMember(root, mUniformBlockOptimizedMap,
mSlowCompilingUniformBlockSet))
{
return false;
}
}
sh::OutputHLSL outputHLSL(
getShaderType(), getShaderSpec(), getShaderVersion(), getExtensionBehavior(),
getSourcePath(), getOutputType(), numRenderTargets, maxDualSourceDrawBuffers, getUniforms(),
extractedSamplerNames, compileOptions, &getSymbolTable(), perfDiagnostics,
mUniformBlockOptimizedMap, getClipDistanceArraySize(), getCullDistanceArraySize(),
isEarlyFragmentTestsSpecified());
outputHLSL.output(root, getInfoSink().obj);
mUniformBlockRegisterMap = outputHLSL.getUniformBlockRegisterMap();
mUniformBlockUseStructuredBufferMap = outputHLSL.getUniformBlockUseStructuredBufferMap();
mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
mReadonlyImage2DRegisterIndex = outputHLSL.getReadonlyImage2DRegisterIndex();
mImage2DRegisterIndex = outputHLSL.getImage2DRegisterIndex();
mUsedImage2DFunctionNames = outputHLSL.getUsedImage2DFunctionNames();
return true;
}
bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll()
{
// Not necessary when translating to HLSL.
return false;
}
bool TranslatorHLSL::hasUniformBlock(const std::string &uniformBlockName) const
{
return (mUniformBlockRegisterMap.count(uniformBlockName) > 0);
}
unsigned int TranslatorHLSL::getUniformBlockRegister(const std::string &uniformBlockName) const
{
ASSERT(hasUniformBlock(uniformBlockName));
return mUniformBlockRegisterMap.find(uniformBlockName)->second;
}
const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
{
return &mUniformRegisterMap;
}
const std::set<std::string> *TranslatorHLSL::getSlowCompilingUniformBlockSet() const
{
return &mSlowCompilingUniformBlockSet;
}
unsigned int TranslatorHLSL::getReadonlyImage2DRegisterIndex() const
{
return mReadonlyImage2DRegisterIndex;
}
unsigned int TranslatorHLSL::getImage2DRegisterIndex() const
{
return mImage2DRegisterIndex;
}
const std::set<std::string> *TranslatorHLSL::getUsedImage2DFunctionNames() const
{
return &mUsedImage2DFunctionNames;
}
bool TranslatorHLSL::shouldUniformBlockUseStructuredBuffer(
const std::string &uniformBlockName) const
{
auto uniformBlockIter = mUniformBlockUseStructuredBufferMap.find(uniformBlockName);
return uniformBlockIter != mUniformBlockUseStructuredBufferMap.end() &&
uniformBlockIter->second;
}
} // namespace sh