blob: 64234345e194fefb1a5572a52ed58a05332e0a21 [file] [log] [blame]
//
// Copyright 2014 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.
//
// StructureHLSL.cpp:
// HLSL translation of GLSL constructors and structures.
//
#include "compiler/translator/StructureHLSL.h"
#include "common/utilities.h"
#include "compiler/translator/OutputHLSL.h"
#include "compiler/translator/Types.h"
#include "compiler/translator/UtilsHLSL.h"
#include "compiler/translator/util.h"
namespace sh
{
namespace
{
TString Define(const TStructure &structure,
bool useHLSLRowMajorPacking,
bool useStd140Packing,
Std140PaddingHelper *padHelper)
{
const TFieldList &fields = structure.fields();
const bool isNameless = (structure.symbolType() == SymbolType::Empty);
const TString &structName =
QualifiedStructNameString(structure, useHLSLRowMajorPacking, useStd140Packing);
const TString declareString = (isNameless ? "struct" : "struct " + structName);
TString string;
string += declareString +
"\n"
"{\n";
for (const TField *field : fields)
{
const TType &fieldType = *field->type();
if (!IsSampler(fieldType.getBasicType()))
{
const TStructure *fieldStruct = fieldType.getStruct();
const TString &fieldTypeString =
fieldStruct ? QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
useStd140Packing)
: TypeString(fieldType);
if (padHelper)
{
string += padHelper->prePaddingString(fieldType);
}
string += " " + fieldTypeString + " " + DecorateField(field->name(), structure) +
ArrayString(fieldType).data() + ";\n";
if (padHelper)
{
string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking);
}
}
}
// Nameless structs do not finish with a semicolon and newline, to leave room for an instance
// variable
string += (isNameless ? "} " : "};\n");
return string;
}
TString WriteParameterList(const std::vector<TType> &parameters)
{
TString parameterList;
for (size_t parameter = 0u; parameter < parameters.size(); parameter++)
{
const TType &paramType = parameters[parameter];
parameterList +=
TypeString(paramType) + " x" + str(parameter) + ArrayString(paramType).data();
if (parameter < parameters.size() - 1u)
{
parameterList += ", ";
}
}
return parameterList;
}
} // anonymous namespace
Std140PaddingHelper::Std140PaddingHelper(const std::map<TString, int> &structElementIndexes,
unsigned *uniqueCounter)
: mPaddingCounter(uniqueCounter), mElementIndex(0), mStructElementIndexes(&structElementIndexes)
{}
Std140PaddingHelper::Std140PaddingHelper(const Std140PaddingHelper &other)
: mPaddingCounter(other.mPaddingCounter),
mElementIndex(other.mElementIndex),
mStructElementIndexes(other.mStructElementIndexes)
{}
Std140PaddingHelper &Std140PaddingHelper::operator=(const Std140PaddingHelper &other)
{
mPaddingCounter = other.mPaddingCounter;
mElementIndex = other.mElementIndex;
mStructElementIndexes = other.mStructElementIndexes;
return *this;
}
TString Std140PaddingHelper::next()
{
unsigned value = (*mPaddingCounter)++;
return str(value);
}
int Std140PaddingHelper::prePadding(const TType &type)
{
if (type.getBasicType() == EbtStruct || type.isMatrix() || type.isArray())
{
// no padding needed, HLSL will align the field to a new register
mElementIndex = 0;
return 0;
}
const GLenum glType = GLVariableType(type);
const int numComponents = gl::VariableComponentCount(glType);
if (numComponents >= 4)
{
// no padding needed, HLSL will align the field to a new register
mElementIndex = 0;
return 0;
}
if (mElementIndex + numComponents > 4)
{
// no padding needed, HLSL will align the field to a new register
mElementIndex = numComponents;
return 0;
}
const int alignment = numComponents == 3 ? 4 : numComponents;
const int paddingOffset = (mElementIndex % alignment);
const int paddingCount = (paddingOffset != 0 ? (alignment - paddingOffset) : 0);
mElementIndex += paddingCount;
mElementIndex += numComponents;
mElementIndex %= 4;
return paddingCount;
}
TString Std140PaddingHelper::prePaddingString(const TType &type)
{
int paddingCount = prePadding(type);
TString padding;
for (int paddingIndex = 0; paddingIndex < paddingCount; paddingIndex++)
{
padding += " float pad_" + next() + ";\n";
}
return padding;
}
TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRowMajorPacking)
{
if (!type.isMatrix() && !type.isArray() && type.getBasicType() != EbtStruct)
{
return "";
}
int numComponents = 0;
const TStructure *structure = type.getStruct();
if (type.isMatrix())
{
// This method can also be called from structureString, which does not use layout
// qualifiers.
// Thus, use the method parameter for determining the matrix packing.
//
// Note HLSL row major packing corresponds to GL API column-major, and vice-versa, since we
// wish to always transpose GL matrices to play well with HLSL's matrix array indexing.
//
const bool isRowMajorMatrix = !useHLSLRowMajorPacking;
const GLenum glType = GLVariableType(type);
numComponents = gl::MatrixComponentCount(glType, isRowMajorMatrix);
}
else if (structure)
{
const TString &structName =
QualifiedStructNameString(*structure, useHLSLRowMajorPacking, true);
numComponents = mStructElementIndexes->find(structName)->second;
if (numComponents == 0)
{
return "";
}
}
else
{
const GLenum glType = GLVariableType(type);
numComponents = gl::VariableComponentCount(glType);
}
TString padding;
for (int paddingOffset = numComponents; paddingOffset < 4; paddingOffset++)
{
padding += " float pad_" + next() + ";\n";
}
return padding;
}
StructureHLSL::StructureHLSL() : mUniquePaddingCounter(0) {}
Std140PaddingHelper StructureHLSL::getPaddingHelper()
{
return Std140PaddingHelper(mStd140StructElementIndexes, &mUniquePaddingCounter);
}
TString StructureHLSL::defineQualified(const TStructure &structure,
bool useHLSLRowMajorPacking,
bool useStd140Packing)
{
if (useStd140Packing)
{
Std140PaddingHelper padHelper = getPaddingHelper();
return Define(structure, useHLSLRowMajorPacking, useStd140Packing, &padHelper);
}
else
{
return Define(structure, useHLSLRowMajorPacking, useStd140Packing, nullptr);
}
}
TString StructureHLSL::defineNameless(const TStructure &structure)
{
return Define(structure, false, false, nullptr);
}
StructureHLSL::DefinedStructs::iterator StructureHLSL::defineVariants(const TStructure &structure,
const TString &name)
{
ASSERT(mDefinedStructs.find(name) == mDefinedStructs.end());
for (const TField *field : structure.fields())
{
const TType *fieldType = field->type();
if (fieldType->getBasicType() == EbtStruct)
{
ensureStructDefined(*fieldType->getStruct());
}
}
DefinedStructs::iterator addedStruct =
mDefinedStructs.insert(std::make_pair(name, new TStructProperties())).first;
// Add element index
storeStd140ElementIndex(structure, false);
storeStd140ElementIndex(structure, true);
const TString &structString = defineQualified(structure, false, false);
ASSERT(std::find(mStructDeclarations.begin(), mStructDeclarations.end(), structString) ==
mStructDeclarations.end());
// Add row-major packed struct for interface blocks
TString rowMajorString = "#pragma pack_matrix(row_major)\n" +
defineQualified(structure, true, false) +
"#pragma pack_matrix(column_major)\n";
TString std140String = defineQualified(structure, false, true);
TString std140RowMajorString = "#pragma pack_matrix(row_major)\n" +
defineQualified(structure, true, true) +
"#pragma pack_matrix(column_major)\n";
mStructDeclarations.push_back(structString);
mStructDeclarations.push_back(rowMajorString);
mStructDeclarations.push_back(std140String);
mStructDeclarations.push_back(std140RowMajorString);
return addedStruct;
}
void StructureHLSL::ensureStructDefined(const TStructure &structure)
{
const TString name = StructNameString(structure);
if (name == "")
{
return; // Nameless structures are not defined
}
if (mDefinedStructs.find(name) == mDefinedStructs.end())
{
defineVariants(structure, name);
}
}
TString StructureHLSL::addStructConstructor(const TStructure &structure)
{
const TString name = StructNameString(structure);
if (name == "")
{
return TString(); // Nameless structures don't have constructors
}
auto definedStruct = mDefinedStructs.find(name);
if (definedStruct == mDefinedStructs.end())
{
definedStruct = defineVariants(structure, name);
}
const TString constructorFunctionName = TString(name) + "_ctor";
TString *constructor = &definedStruct->second->constructor;
if (!constructor->empty())
{
return constructorFunctionName; // Already added
}
*constructor += name + " " + constructorFunctionName + "(";
std::vector<TType> ctorParameters;
const TFieldList &fields = structure.fields();
for (const TField *field : fields)
{
const TType *fieldType = field->type();
if (!IsSampler(fieldType->getBasicType()))
{
ctorParameters.push_back(*fieldType);
}
}
// Structs that have sampler members should not have constructor calls, and otherwise structs
// are guaranteed to be non-empty by the grammar. Structs can't contain empty declarations
// either.
ASSERT(!ctorParameters.empty());
*constructor += WriteParameterList(ctorParameters);
*constructor +=
")\n"
"{\n"
" " +
name + " structure = { ";
for (size_t parameterIndex = 0u; parameterIndex < ctorParameters.size(); ++parameterIndex)
{
*constructor += "x" + str(parameterIndex);
if (parameterIndex < ctorParameters.size() - 1u)
{
*constructor += ", ";
}
}
*constructor +=
"};\n"
" return structure;\n"
"}\n";
return constructorFunctionName;
}
TString StructureHLSL::addBuiltInConstructor(const TType &type, const TIntermSequence *parameters)
{
ASSERT(!type.isArray());
ASSERT(type.getStruct() == nullptr);
ASSERT(parameters);
TType ctorType = type;
ctorType.setPrecision(EbpHigh);
ctorType.setQualifier(EvqTemporary);
const TString constructorFunctionName =
TString(type.getBuiltInTypeNameString()) + "_ctor" + DisambiguateFunctionName(parameters);
TString constructor = TypeString(ctorType) + " " + constructorFunctionName + "(";
std::vector<TType> ctorParameters;
for (auto parameter : *parameters)
{
const TType &paramType = parameter->getAsTyped()->getType();
ASSERT(!paramType.isArray());
ctorParameters.push_back(paramType);
}
constructor += WriteParameterList(ctorParameters);
constructor +=
")\n"
"{\n"
" return " +
TypeString(ctorType) + "(";
if (ctorType.isMatrix() && ctorParameters.size() == 1)
{
int rows = ctorType.getRows();
int cols = ctorType.getCols();
const TType &parameter = ctorParameters[0];
if (parameter.isScalar())
{
for (int col = 0; col < cols; col++)
{
for (int row = 0; row < rows; row++)
{
constructor += TString((row == col) ? "x0" : "0.0");
if (row < rows - 1 || col < cols - 1)
{
constructor += ", ";
}
}
}
}
else if (parameter.isMatrix())
{
for (int col = 0; col < cols; col++)
{
for (int row = 0; row < rows; row++)
{
if (row < parameter.getRows() && col < parameter.getCols())
{
constructor += TString("x0") + "[" + str(col) + "][" + str(row) + "]";
}
else
{
constructor += TString((row == col) ? "1.0" : "0.0");
}
if (row < rows - 1 || col < cols - 1)
{
constructor += ", ";
}
}
}
}
else
{
ASSERT(rows == 2 && cols == 2 && parameter.isVector() &&
parameter.getNominalSize() == 4);
constructor += "x0";
}
}
else
{
size_t remainingComponents = ctorType.getObjectSize();
size_t parameterIndex = 0;
while (remainingComponents > 0)
{
const TType &parameter = ctorParameters[parameterIndex];
const size_t parameterSize = parameter.getObjectSize();
bool moreParameters = parameterIndex + 1 < ctorParameters.size();
constructor += "x" + str(parameterIndex);
if (parameter.isScalar())
{
remainingComponents -= parameter.getObjectSize();
}
else if (parameter.isVector())
{
if (remainingComponents == parameterSize || moreParameters)
{
ASSERT(parameterSize <= remainingComponents);
remainingComponents -= parameterSize;
}
else if (remainingComponents < static_cast<size_t>(parameter.getNominalSize()))
{
switch (remainingComponents)
{
case 1:
constructor += ".x";
break;
case 2:
constructor += ".xy";
break;
case 3:
constructor += ".xyz";
break;
case 4:
constructor += ".xyzw";
break;
default:
UNREACHABLE();
}
remainingComponents = 0;
}
else
UNREACHABLE();
}
else if (parameter.isMatrix())
{
int column = 0;
while (remainingComponents > 0 && column < parameter.getCols())
{
constructor += "[" + str(column) + "]";
if (remainingComponents < static_cast<size_t>(parameter.getRows()))
{
switch (remainingComponents)
{
case 1:
constructor += ".x";
break;
case 2:
constructor += ".xy";
break;
case 3:
constructor += ".xyz";
break;
default:
UNREACHABLE();
}
remainingComponents = 0;
}
else
{
remainingComponents -= parameter.getRows();
if (remainingComponents > 0)
{
constructor += ", x" + str(parameterIndex);
}
}
column++;
}
}
else
{
UNREACHABLE();
}
if (moreParameters)
{
parameterIndex++;
}
if (remainingComponents)
{
constructor += ", ";
}
}
}
constructor +=
");\n"
"}\n";
mBuiltInConstructors.insert(constructor);
return constructorFunctionName;
}
std::string StructureHLSL::structsHeader() const
{
TInfoSinkBase out;
for (auto &declaration : mStructDeclarations)
{
out << declaration;
}
for (auto &structure : mDefinedStructs)
{
out << structure.second->constructor;
}
for (auto &constructor : mBuiltInConstructors)
{
out << constructor;
}
return out.str();
}
void StructureHLSL::storeStd140ElementIndex(const TStructure &structure,
bool useHLSLRowMajorPacking)
{
Std140PaddingHelper padHelper = getPaddingHelper();
const TFieldList &fields = structure.fields();
for (const TField *field : fields)
{
padHelper.prePadding(*field->type());
}
// Add remaining element index to the global map, for use with nested structs in standard
// layouts
const TString &structName = QualifiedStructNameString(structure, useHLSLRowMajorPacking, true);
mStd140StructElementIndexes[structName] = padHelper.elementIndex();
}
} // namespace sh