blob: ebb0f3b4edbfca8ebf85274fc6e26895e39beee0 [file] [log] [blame]
//
// Copyright 2015 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.
//
// VaryingPacking:
// Class which describes a mapping from varyings to registers, according
// to the spec, or using custom packing algorithms. We also keep a register
// allocation list for the D3D renderer.
//
#ifndef LIBANGLE_VARYINGPACKING_H_
#define LIBANGLE_VARYINGPACKING_H_
#include <GLSLANG/ShaderVars.h>
#include "angle_gl.h"
#include "common/angleutils.h"
#include "libANGLE/angletypes.h"
#include <map>
namespace gl
{
class InfoLog;
class ProgramExecutable;
struct Caps;
struct LinkingVariables;
struct ProgramVaryingRef;
using ProgramMergedVaryings = std::vector<ProgramVaryingRef>;
// A varying can have different names between stages if matched by the location layout qualifier.
// Additionally, same name varyings could still be of two identical struct types with different
// names. This struct contains information on the varying in one of the two stages. PackedVarying
// will thus contain two copies of this along with common information, such as interpolation or
// field index.
struct VaryingInShaderRef : angle::NonCopyable
{
VaryingInShaderRef(ShaderType stageIn, const sh::ShaderVariable *varyingIn);
VaryingInShaderRef(VaryingInShaderRef &&other);
~VaryingInShaderRef();
VaryingInShaderRef &operator=(VaryingInShaderRef &&other);
const sh::ShaderVariable *varying;
ShaderType stage;
// Struct name
std::string parentStructName;
std::string parentStructMappedName;
};
struct PackedVarying : angle::NonCopyable
{
// Throughout this file, the "front" stage refers to the stage that outputs the varying, and the
// "back" stage refers to the stage that takes the varying as input. Note that this struct
// contains linked varyings, which means both front and back stage varyings are valid, except
// for the following which may have only one valid stage.
//
// - transform-feedback-captured varyings
// - builtins
// - separable program stages,
//
PackedVarying(VaryingInShaderRef &&frontVaryingIn,
VaryingInShaderRef &&backVaryingIn,
sh::InterpolationType interpolationIn);
PackedVarying(VaryingInShaderRef &&frontVaryingIn,
VaryingInShaderRef &&backVaryingIn,
sh::InterpolationType interpolationIn,
GLuint arrayIndexIn,
GLuint fieldIndexIn,
GLuint secondaryFieldIndexIn);
PackedVarying(PackedVarying &&other);
~PackedVarying();
PackedVarying &operator=(PackedVarying &&other);
bool isStructField() const
{
return frontVarying.varying ? !frontVarying.parentStructName.empty()
: !backVarying.parentStructName.empty();
}
bool isTransformFeedbackArrayElement() const
{
return isTransformFeedback && arrayIndex != GL_INVALID_INDEX;
}
// Return either front or back varying, whichever is available. Only used when the name of the
// varying is not important, but only the type is interesting.
const sh::ShaderVariable &varying() const
{
return frontVarying.varying ? *frontVarying.varying : *backVarying.varying;
}
const std::string &getParentStructName() const
{
ASSERT(isStructField());
return frontVarying.varying ? frontVarying.parentStructName : backVarying.parentStructName;
}
std::string fullName(ShaderType stage) const
{
ASSERT(stage == frontVarying.stage || stage == backVarying.stage);
const VaryingInShaderRef &varying =
stage == frontVarying.stage ? frontVarying : backVarying;
std::stringstream fullNameStr;
if (isStructField())
{
fullNameStr << varying.parentStructName << ".";
}
fullNameStr << varying.varying->name;
if (arrayIndex != GL_INVALID_INDEX)
{
fullNameStr << "[" << arrayIndex << "]";
}
return fullNameStr.str();
}
// Transform feedback varyings can be only referenced in the VS.
bool vertexOnly() const
{
return frontVarying.stage == ShaderType::Vertex && backVarying.varying == nullptr;
}
// Special handling for GS/TS array inputs.
unsigned int getBasicTypeElementCount() const;
VaryingInShaderRef frontVarying;
VaryingInShaderRef backVarying;
// Cached so we can store sh::ShaderVariable to point to varying fields.
sh::InterpolationType interpolation;
// Used by varyings that are captured with transform feedback, xor arrays of shader I/O blocks,
// distinguished by isTransformFeedback;
GLuint arrayIndex;
bool isTransformFeedback;
// Field index in the struct. In Vulkan, this is used to assign a
// struct-typed varying location to the location of its first field.
GLuint fieldIndex;
GLuint secondaryFieldIndex;
};
struct PackedVaryingRegister final
{
PackedVaryingRegister()
: packedVarying(nullptr),
varyingArrayIndex(0),
varyingRowIndex(0),
registerRow(0),
registerColumn(0)
{}
PackedVaryingRegister(const PackedVaryingRegister &) = default;
PackedVaryingRegister &operator=(const PackedVaryingRegister &) = default;
bool operator<(const PackedVaryingRegister &other) const
{
return sortOrder() < other.sortOrder();
}
unsigned int sortOrder() const
{
// TODO(jmadill): Handle interpolation types
return registerRow * 4 + registerColumn;
}
std::string tfVaryingName() const
{
return packedVarying->fullName(packedVarying->frontVarying.stage);
}
// Index to the array of varyings.
const PackedVarying *packedVarying;
// The array element of the packed varying.
unsigned int varyingArrayIndex;
// The row of the array element of the packed varying.
unsigned int varyingRowIndex;
// The register row to which we've assigned this packed varying.
unsigned int registerRow;
// The column of the register row into which we've packed this varying.
unsigned int registerColumn;
};
// Supported packing modes:
enum class PackMode
{
// We treat mat2 arrays as taking two full rows.
WEBGL_STRICT,
// We allow mat2 to take a 2x2 chunk.
ANGLE_RELAXED,
// Each varying takes a separate register. No register sharing.
ANGLE_NON_CONFORMANT_D3D9,
};
class VaryingPacking final : angle::NonCopyable
{
public:
VaryingPacking();
~VaryingPacking();
ANGLE_NO_DISCARD bool collectAndPackUserVaryings(InfoLog &infoLog,
GLint maxVaryingVectors,
PackMode packMode,
ShaderType frontShaderStage,
ShaderType backShaderStage,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings,
const bool isSeparableProgram);
struct Register
{
Register() { data[0] = data[1] = data[2] = data[3] = false; }
bool &operator[](unsigned int index) { return data[index]; }
bool operator[](unsigned int index) const { return data[index]; }
bool data[4];
};
Register &operator[](unsigned int index) { return mRegisterMap[index]; }
const Register &operator[](unsigned int index) const { return mRegisterMap[index]; }
const std::vector<PackedVaryingRegister> &getRegisterList() const { return mRegisterList; }
unsigned int getMaxSemanticIndex() const
{
return static_cast<unsigned int>(mRegisterList.size());
}
const ShaderMap<std::vector<std::string>> &getInactiveVaryingMappedNames() const
{
return mInactiveVaryingMappedNames;
}
const ShaderMap<std::vector<std::string>> &getActiveOutputBuiltInNames() const
{
return mActiveOutputBuiltIns;
}
void reset();
private:
using VaryingUniqueFullNames = ShaderMap<std::set<std::string>>;
// Register map functions.
bool packUserVaryings(InfoLog &infoLog,
GLint maxVaryingVectors,
PackMode packMode,
const std::vector<PackedVarying> &packedVaryings);
bool packVaryingIntoRegisterMap(PackMode packMode, const PackedVarying &packedVarying);
bool isRegisterRangeFree(unsigned int registerRow,
unsigned int registerColumn,
unsigned int varyingRows,
unsigned int varyingColumns) const;
void insertVaryingIntoRegisterMap(unsigned int registerRow,
unsigned int registerColumn,
unsigned int varyingColumns,
const PackedVarying &packedVarying);
void clearRegisterMap();
// Collection functions.
void collectUserVarying(const ProgramVaryingRef &ref, VaryingUniqueFullNames *uniqueFullNames);
void collectUserVaryingField(const ProgramVaryingRef &ref,
GLuint arrayIndex,
GLuint fieldIndex,
GLuint secondaryFieldIndex,
VaryingUniqueFullNames *uniqueFullNames);
void collectUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript);
void collectUserVaryingFieldTF(const ProgramVaryingRef &ref,
const sh::ShaderVariable &field,
GLuint fieldIndex,
GLuint secondaryFieldIndex);
void collectVarying(const sh::ShaderVariable &varying,
const ProgramVaryingRef &ref,
PackMode packMode,
VaryingUniqueFullNames *uniqueFullNames);
void collectTFVarying(const std::string &tfVarying,
const ProgramVaryingRef &ref,
VaryingUniqueFullNames *uniqueFullNames);
std::vector<Register> mRegisterMap;
std::vector<PackedVaryingRegister> mRegisterList;
std::vector<PackedVarying> mPackedVaryings;
ShaderMap<std::vector<std::string>> mInactiveVaryingMappedNames;
ShaderMap<std::vector<std::string>> mActiveOutputBuiltIns;
};
class ProgramVaryingPacking final : angle::NonCopyable
{
public:
ProgramVaryingPacking();
~ProgramVaryingPacking();
const VaryingPacking &getInputPacking(ShaderType backShaderStage) const;
const VaryingPacking &getOutputPacking(ShaderType frontShaderStage) const;
ANGLE_NO_DISCARD bool collectAndPackUserVaryings(InfoLog &infoLog,
const Caps &caps,
PackMode packMode,
const ShaderBitSet &activeShadersMask,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings,
bool isSeparableProgram);
private:
// Indexed by the front shader.
ShaderMap<VaryingPacking> mVaryingPackings;
// Looks up the front stage from the back stage.
ShaderMap<ShaderType> mBackToFrontStageMap;
};
ProgramMergedVaryings GetMergedVaryingsFromLinkingVariables(
const LinkingVariables &linkingVariables);
} // namespace gl
#endif // LIBANGLE_VARYINGPACKING_H_