| // | |
| // 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> ¶meters) | |
| { | |
| TString parameterList; | |
| for (size_t parameter = 0u; parameter < parameters.size(); parameter++) | |
| { | |
| const TType ¶mType = 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 ¶mType = 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 ¶meter = 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 ¶meter = 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 |