Commit f764fc02 by Shahbaz Youssefi Committed by Commit Bot

Fix varying linking by location

This change breaks the assumption everywhere that varyings can be identified uniquely by name throughout all stages of the pipeline. It further implements linking of varyings by location, if specified. Bug: angleproject:4355 Change-Id: Ie45e48879008c3f0c22d1da3d0d26f37c655e54e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2030026 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 58fc8b11
......@@ -405,13 +405,19 @@ void InitShaderStorageBlockLinker(const ProgramState &state, ShaderStorageBlockL
}
// Find the matching varying or field by name.
const sh::ShaderVariable *FindVaryingOrField(const ProgramMergedVaryings &varyings,
const std::string &name)
const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &varyings,
ShaderType stage,
const std::string &name)
{
const sh::ShaderVariable *var = nullptr;
for (const auto &ref : varyings)
for (const ProgramVaryingRef &ref : varyings)
{
const sh::ShaderVariable *varying = ref.second.get();
if (ref.frontShaderStage != stage)
{
continue;
}
const sh::ShaderVariable *varying = ref.get(stage);
if (varying->name == name)
{
var = varying;
......@@ -1583,7 +1589,7 @@ angle::Result Program::link(const Context *context)
return angle::Result::Continue;
}
const auto &mergedVaryings = getMergedVaryings();
const ProgramMergedVaryings &mergedVaryings = getMergedVaryings();
gl::Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
if (vertexShader)
......@@ -1594,8 +1600,11 @@ angle::Result Program::link(const Context *context)
InitUniformBlockLinker(mState, &resources->uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &resources->shaderStorageBlockLinker);
ShaderType tfStage = mState.mAttachedShaders[ShaderType::Geometry] ? ShaderType::Geometry
: ShaderType::Vertex;
if (!linkValidateTransformFeedback(context->getClientVersion(), mInfoLog, mergedVaryings,
context->getCaps()))
tfStage, context->getCaps()))
{
return angle::Result::Continue;
}
......@@ -1606,7 +1615,7 @@ angle::Result Program::link(const Context *context)
return angle::Result::Continue;
}
gatherTransformFeedbackVaryings(mergedVaryings);
gatherTransformFeedbackVaryings(mergedVaryings, tfStage);
mState.updateTransformFeedbackStrides();
}
......@@ -4107,6 +4116,7 @@ bool Program::linkValidateBuiltInVaryings(InfoLog &infoLog) const
bool Program::linkValidateTransformFeedback(const Version &version,
InfoLog &infoLog,
const ProgramMergedVaryings &varyings,
ShaderType stage,
const Caps &caps) const
{
......@@ -4147,7 +4157,7 @@ bool Program::linkValidateTransformFeedback(const Version &version,
std::vector<unsigned int> subscripts;
std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
const sh::ShaderVariable *var = FindVaryingOrField(varyings, baseName);
const sh::ShaderVariable *var = FindOutputVaryingOrField(varyings, stage, baseName);
if (var == nullptr)
{
infoLog << "Transform feedback varying " << tfVaryingName
......@@ -4328,7 +4338,8 @@ bool Program::linkValidateGlobalNames(InfoLog &infoLog) const
return true;
}
void Program::gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings)
void Program::gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings,
ShaderType stage)
{
// Gather the linked varyings that are used for transform feedback, they should all exist.
mState.mLinkedTransformFeedbackVaryings.clear();
......@@ -4341,9 +4352,14 @@ void Program::gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyi
{
subscript = subscripts.back();
}
for (const auto &ref : varyings)
for (const ProgramVaryingRef &ref : varyings)
{
const sh::ShaderVariable *varying = ref.second.get();
if (ref.frontShaderStage != stage)
{
continue;
}
const sh::ShaderVariable *varying = ref.get(stage);
if (baseName == varying->name)
{
mState.mLinkedTransformFeedbackVaryings.emplace_back(
......@@ -4366,25 +4382,113 @@ void Program::gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyi
ProgramMergedVaryings Program::getMergedVaryings() const
{
ASSERT(mState.mAttachedShaders[ShaderType::Compute] == nullptr);
// Varyings are matched between pairs of consecutive stages, by location if assigned or
// by name otherwise. Note that it's possible for one stage to specify location and the other
// not: https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16261
// Map stages to the previous active stage in the rendering pipeline. When looking at input
// varyings of a stage, this is used to find the stage whose output varyings are being linked
// with them.
ShaderMap<ShaderType> previousActiveStage;
// Note that kAllGraphicsShaderTypes is sorted according to the rendering pipeline.
ShaderType lastActiveStage = ShaderType::InvalidEnum;
for (ShaderType stage : kAllGraphicsShaderTypes)
{
previousActiveStage[stage] = lastActiveStage;
if (mState.mAttachedShaders[stage])
{
lastActiveStage = stage;
}
}
// First, go through output varyings and create two maps (one by name, one by location) for
// faster lookup when matching input varyings.
ShaderMap<std::map<std::string, size_t>> outputVaryingNameToIndex;
ShaderMap<std::map<int, size_t>> outputVaryingLocationToIndex;
ProgramMergedVaryings merged;
// Gather output varyings.
for (Shader *shader : mState.mAttachedShaders)
{
if (shader)
if (!shader)
{
continue;
}
ShaderType stage = shader->getType();
for (const sh::ShaderVariable &varying : shader->getOutputVaryings())
{
merged.push_back({});
ProgramVaryingRef *ref = &merged.back();
ref->frontShader = &varying;
ref->frontShaderStage = stage;
// Always map by name. Even if location is provided in this stage, it may not be in the
// paired stage.
outputVaryingNameToIndex[stage][varying.name] = merged.size() - 1;
// If location is provided, also keep it in a map by location.
if (varying.location != -1)
{
outputVaryingLocationToIndex[stage][varying.location] = merged.size() - 1;
}
}
}
// Gather input varyings, and match them with output varyings of the previous stage.
for (Shader *shader : mState.mAttachedShaders)
{
if (!shader)
{
ShaderType shaderType = shader->getType();
for (const sh::ShaderVariable &varying : shader->getOutputVaryings())
continue;
}
ShaderType stage = shader->getType();
ShaderType previousStage = previousActiveStage[stage];
for (const sh::ShaderVariable &varying : shader->getInputVaryings())
{
size_t mergedIndex = merged.size();
if (previousStage != ShaderType::InvalidEnum)
{
ProgramVaryingRef *ref = &merged[varying.name];
ref->frontShader = &varying;
ref->frontShaderStage = shaderType;
// If location is provided, see if we can match by location.
if (varying.location != -1)
{
auto byLocationIter =
outputVaryingLocationToIndex[previousStage].find(varying.location);
if (byLocationIter != outputVaryingLocationToIndex[previousStage].end())
{
mergedIndex = byLocationIter->second;
}
}
// If not found, try to match by name.
if (mergedIndex == merged.size())
{
auto byNameIter = outputVaryingNameToIndex[previousStage].find(varying.name);
if (byNameIter != outputVaryingNameToIndex[previousStage].end())
{
mergedIndex = byNameIter->second;
}
}
}
for (const sh::ShaderVariable &varying : shader->getInputVaryings())
// If no previous stage, or not matched by location or name, create a new entry for it.
if (mergedIndex == merged.size())
{
ProgramVaryingRef *ref = &merged[varying.name];
ref->backShader = &varying;
ref->backShaderStage = shaderType;
merged.push_back({});
mergedIndex = merged.size() - 1;
}
ProgramVaryingRef *ref = &merged[mergedIndex];
ref->backShader = &varying;
ref->backShaderStage = stage;
}
}
......
......@@ -568,7 +568,13 @@ class ProgramAliasedBindings final : angle::NonCopyable
struct ProgramVaryingRef
{
const sh::ShaderVariable *get() const { return frontShader ? frontShader : backShader; }
const sh::ShaderVariable *get(ShaderType stage) const
{
ASSERT(stage == frontShaderStage || stage == backShaderStage);
const sh::ShaderVariable *ref = stage == frontShaderStage ? frontShader : backShader;
ASSERT(ref);
return ref;
}
const sh::ShaderVariable *frontShader = nullptr;
const sh::ShaderVariable *backShader = nullptr;
......@@ -576,7 +582,7 @@ struct ProgramVaryingRef
ShaderType backShaderStage = ShaderType::InvalidEnum;
};
using ProgramMergedVaryings = std::map<std::string, ProgramVaryingRef>;
using ProgramMergedVaryings = std::vector<ProgramVaryingRef>;
class Program final : angle::NonCopyable, public LabeledObject
{
......@@ -1025,10 +1031,11 @@ class Program final : angle::NonCopyable, public LabeledObject
bool linkValidateTransformFeedback(const Version &version,
InfoLog &infoLog,
const ProgramMergedVaryings &linkedVaryings,
ShaderType stage,
const Caps &caps) const;
bool linkValidateGlobalNames(InfoLog &infoLog) const;
void gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings);
void gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings, ShaderType stage);
ProgramMergedVaryings getMergedVaryings() const;
int getOutputLocationForLink(const sh::ShaderVariable &outputVariable) const;
......
......@@ -28,47 +28,65 @@ bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
// non-array shader variable 'vx' or 'vy' for actual comparison instead.
sh::ShaderVariable vx, vy;
const sh::ShaderVariable *px, *py;
px = &x.varying();
py = &y.varying();
if (x.isArrayElement())
{
vx = *x.varying;
vx = *px;
vx.arraySizes.clear();
px = &vx;
}
else
{
px = x.varying;
}
if (y.isArrayElement())
{
vy = *y.varying;
vy = *py;
vy.arraySizes.clear();
py = &vy;
}
else
{
py = y.varying;
}
return gl::CompareShaderVar(*px, *py);
}
} // anonymous namespace
// Implementation of VaryingInShaderRef
VaryingInShaderRef::VaryingInShaderRef(ShaderType stageIn, const sh::ShaderVariable *varyingIn)
: varying(varyingIn), stage(stageIn)
{}
VaryingInShaderRef::~VaryingInShaderRef() = default;
VaryingInShaderRef::VaryingInShaderRef(VaryingInShaderRef &&other)
{
*this = std::move(other);
}
VaryingInShaderRef &VaryingInShaderRef::operator=(VaryingInShaderRef &&other)
{
std::swap(varying, other.varying);
std::swap(stage, other.stage);
std::swap(parentStructName, other.parentStructName);
std::swap(parentStructMappedName, other.parentStructMappedName);
return *this;
}
// Implementation of PackedVarying
PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn,
PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
VaryingInShaderRef &&backVaryingIn,
sh::InterpolationType interpolationIn)
: PackedVarying(varyingIn, interpolationIn, "", "", false)
: PackedVarying(std::move(frontVaryingIn), std::move(backVaryingIn), interpolationIn, 0)
{}
PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn,
PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
VaryingInShaderRef &&backVaryingIn,
sh::InterpolationType interpolationIn,
const std::string &parentStructNameIn,
const std::string &parentStructMappedNameIn,
GLuint fieldIndexIn)
: varying(&varyingIn),
: frontVarying(std::move(frontVaryingIn)),
backVarying(std::move(backVaryingIn)),
interpolation(interpolationIn),
parentStructName(parentStructNameIn),
parentStructMappedName(parentStructMappedNameIn),
arrayIndex(GL_INVALID_INDEX),
fieldIndex(fieldIndexIn)
{}
......@@ -76,17 +94,17 @@ PackedVarying::PackedVarying(const sh::ShaderVariable &varyingIn,
PackedVarying::~PackedVarying() = default;
PackedVarying::PackedVarying(PackedVarying &&other)
: PackedVarying(*other.varying, other.interpolation)
: frontVarying(other.frontVarying.stage, other.frontVarying.varying),
backVarying(other.backVarying.stage, other.backVarying.varying)
{
*this = std::move(other);
}
PackedVarying &PackedVarying::operator=(PackedVarying &&other)
{
std::swap(varying, other.varying);
std::swap(shaderStages, other.shaderStages);
std::swap(frontVarying, other.frontVarying);
std::swap(backVarying, other.backVarying);
std::swap(interpolation, other.interpolation);
std::swap(parentStructName, other.parentStructName);
std::swap(parentStructMappedName, other.parentStructMappedName);
std::swap(arrayIndex, other.arrayIndex);
std::swap(fieldIndex, other.fieldIndex);
......@@ -106,7 +124,7 @@ VaryingPacking::~VaryingPacking() = default;
// Returns false if unsuccessful.
bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
{
const sh::ShaderVariable &varying = *packedVarying.varying;
const sh::ShaderVariable &varying = packedVarying.varying();
// "Non - square matrices of type matCxR consume the same space as a square matrix of type matN
// where N is the greater of C and R."
......@@ -234,7 +252,7 @@ bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
registerInfo.varyingRowIndex = 0;
// Do not record register info for builtins.
// TODO(jmadill): Clean this up.
if (!packedVarying.varying->isBuiltIn())
if (!varying.isBuiltIn())
{
mRegisterList.push_back(registerInfo);
}
......@@ -277,7 +295,7 @@ void VaryingPacking::insert(unsigned int registerRow,
unsigned int varyingRows = 0;
unsigned int varyingColumns = 0;
const auto &varying = *packedVarying.varying;
const sh::ShaderVariable &varying = packedVarying.varying();
ASSERT(!varying.isStruct());
GLenum transposedType = gl::TransposeMatrixType(varying.type);
varyingRows = gl::VariableRowCount(transposedType);
......@@ -303,7 +321,7 @@ void VaryingPacking::insert(unsigned int registerRow,
registerInfo.varyingArrayIndex = arrayElement;
// Do not record register info for builtins.
// TODO(jmadill): Clean this up.
if (!packedVarying.varying->isBuiltIn())
if (!varying.isBuiltIn())
{
mRegisterList.push_back(registerInfo);
}
......@@ -316,26 +334,114 @@ void VaryingPacking::insert(unsigned int registerRow,
}
}
void VaryingPacking::packUserVarying(const ProgramVaryingRef &ref,
VaryingUniqueFullNames *uniqueFullNames)
{
const sh::ShaderVariable *input = ref.frontShader;
const sh::ShaderVariable *output = ref.backShader;
// Will get the vertex shader interpolation by default.
sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
VaryingInShaderRef backVarying(ref.backShaderStage, output);
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation);
if (input)
{
(*uniqueFullNames)[ref.frontShaderStage].insert(
mPackedVaryings.back().fullName(ref.frontShaderStage));
}
if (output)
{
(*uniqueFullNames)[ref.backShaderStage].insert(
mPackedVaryings.back().fullName(ref.backShaderStage));
}
}
void VaryingPacking::packUserVaryingField(const ProgramVaryingRef &ref,
GLuint fieldIndex,
VaryingUniqueFullNames *uniqueFullNames)
{
const sh::ShaderVariable *input = ref.frontShader;
const sh::ShaderVariable *output = ref.backShader;
// Will get the vertex shader interpolation by default.
sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
const sh::ShaderVariable *frontField = input ? &input->fields[fieldIndex] : nullptr;
const sh::ShaderVariable *backField = output ? &output->fields[fieldIndex] : nullptr;
VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
VaryingInShaderRef backVarying(ref.backShaderStage, backField);
if (input)
{
ASSERT(!frontField->isStruct() && !frontField->isArray());
frontVarying.parentStructName = input->name;
frontVarying.parentStructMappedName = input->mappedName;
}
if (output)
{
ASSERT(!backField->isStruct() && !backField->isArray());
backVarying.parentStructName = output->name;
backVarying.parentStructMappedName = output->mappedName;
}
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation,
fieldIndex);
if (input)
{
(*uniqueFullNames)[ref.frontShaderStage].insert(
mPackedVaryings.back().fullName(ref.frontShaderStage));
}
if (output)
{
(*uniqueFullNames)[ref.backShaderStage].insert(
mPackedVaryings.back().fullName(ref.backShaderStage));
}
}
void VaryingPacking::packUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript)
{
const sh::ShaderVariable *input = ref.frontShader;
VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
input->interpolation);
mPackedVaryings.back().arrayIndex = static_cast<GLuint>(subscript);
}
void VaryingPacking::packUserVaryingFieldTF(const ProgramVaryingRef &ref,
const sh::ShaderVariable &field,
GLuint fieldIndex)
{
const sh::ShaderVariable *input = ref.frontShader;
VaryingInShaderRef frontVarying(ref.frontShaderStage, &field);
VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
frontVarying.parentStructName = input->name;
frontVarying.parentStructMappedName = input->mappedName;
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
input->interpolation, fieldIndex);
}
bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings)
{
std::set<std::string> uniqueFullNames;
VaryingUniqueFullNames uniqueFullNames;
mPackedVaryings.clear();
mInputVaryings.clear();
for (const auto &ref : mergedVaryings)
for (const ProgramVaryingRef &ref : mergedVaryings)
{
const sh::ShaderVariable *input = ref.second.frontShader;
const sh::ShaderVariable *output = ref.second.backShader;
if (input)
{
mInputVaryings.emplace_back(*input);
}
ShaderBitSet shaderStages;
shaderStages.set(ref.second.frontShaderStage);
shaderStages.set(ref.second.backShaderStage);
const sh::ShaderVariable *input = ref.frontShader;
const sh::ShaderVariable *output = ref.backShader;
// Only pack statically used varyings that have a matched input or output, plus special
// builtins. Note that we pack all statically used user-defined varyings even if they are
......@@ -353,31 +459,26 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
!(varying->name == "gl_PointSize" &&
mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9))
{
// Will get the vertex shader interpolation by default.
auto interpolation = ref.second.get()->interpolation;
// Note that we lose the vertex shader static use information here. The data for the
// variable is taken from the fragment shader.
if (varying->isStruct())
{
ASSERT(!(varying->isArray() && varying == input));
for (GLuint fieldIndex = 0; fieldIndex < varying->fields.size(); ++fieldIndex)
{
const sh::ShaderVariable &field = varying->fields[fieldIndex];
ASSERT(!field.isStruct() && !field.isArray());
mPackedVaryings.emplace_back(field, interpolation, varying->name,
varying->mappedName, fieldIndex);
mPackedVaryings.back().shaderStages = shaderStages;
uniqueFullNames.insert(mPackedVaryings.back().fullName());
packUserVaryingField(ref, fieldIndex, &uniqueFullNames);
}
if (input)
{
uniqueFullNames[ref.frontShaderStage].insert(input->name);
}
if (output)
{
uniqueFullNames[ref.backShaderStage].insert(output->name);
}
uniqueFullNames.insert(varying->name);
}
else
{
mPackedVaryings.emplace_back(*varying, interpolation);
mPackedVaryings.back().shaderStages = shaderStages;
uniqueFullNames.insert(mPackedVaryings.back().fullName());
packUserVarying(ref, &uniqueFullNames);
}
continue;
}
......@@ -386,7 +487,7 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
// If the varying is not used in the input, we know it is inactive.
if (!input)
{
mInactiveVaryingMappedNames.push_back(output->mappedName);
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
continue;
}
......@@ -401,7 +502,8 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
subscript = subscripts.back();
}
// Already packed for fragment shader.
if (uniqueFullNames.count(tfVarying) > 0 || uniqueFullNames.count(baseName) > 0)
if (uniqueFullNames[ref.frontShaderStage].count(tfVarying) > 0 ||
uniqueFullNames[ref.frontShaderStage].count(baseName) > 0)
{
continue;
}
......@@ -413,13 +515,11 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
if (field != nullptr)
{
ASSERT(!field->isStruct() && !field->isArray());
mPackedVaryings.emplace_back(*field, input->interpolation, input->name,
input->mappedName, fieldIndex);
mPackedVaryings.back().shaderStages.set(ShaderType::Vertex);
mPackedVaryings.back().arrayIndex = GL_INVALID_INDEX;
uniqueFullNames.insert(tfVarying);
packUserVaryingFieldTF(ref, *field, fieldIndex);
uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
}
uniqueFullNames.insert(input->name);
uniqueFullNames[ref.frontShaderStage].insert(input->name);
}
// Array as a whole and array element conflict has already been checked in
// linkValidateTransformFeedback.
......@@ -428,10 +528,8 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
// only pack varyings that are not builtins.
if (tfVarying.compare(0, 3, "gl_") != 0)
{
mPackedVaryings.emplace_back(*input, input->interpolation);
mPackedVaryings.back().shaderStages.set(ShaderType::Vertex);
mPackedVaryings.back().arrayIndex = static_cast<GLuint>(subscript);
uniqueFullNames.insert(tfVarying);
packUserVaryingTF(ref, subscript);
uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
}
// Continue to match next array element for 'input' if the current match is array
// element.
......@@ -442,9 +540,13 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
}
}
if (uniqueFullNames.count(ref.first) == 0)
if (input && uniqueFullNames[ref.frontShaderStage].count(input->name) == 0)
{
mInactiveVaryingMappedNames[ref.frontShaderStage].push_back(input->mappedName);
}
if (output && uniqueFullNames[ref.backShaderStage].count(output->name) == 0)
{
mInactiveVaryingMappedNames.push_back(input->mappedName);
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
}
}
......@@ -463,7 +565,10 @@ bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
{
if (!packVarying(packedVarying))
{
infoLog << "Could not pack varying " << packedVarying.fullName();
ShaderType eitherStage = packedVarying.frontVarying.varying
? packedVarying.frontVarying.stage
: packedVarying.backVarying.stage;
infoLog << "Could not pack varying " << packedVarying.fullName(eitherStage);
// TODO(jmadill): Implement more sophisticated component packing in D3D9.
if (mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
......
......@@ -25,34 +25,77 @@ namespace gl
class InfoLog;
struct ProgramVaryingRef;
using ProgramMergedVaryings = std::map<std::string, 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
{
PackedVarying(const sh::ShaderVariable &varyingIn, sh::InterpolationType interpolationIn);
PackedVarying(const sh::ShaderVariable &varyingIn,
// 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,
const std::string &parentStructNameIn,
const std::string &parentStructMappedNameIn,
GLuint fieldIndexIn);
PackedVarying(PackedVarying &&other);
~PackedVarying();
PackedVarying &operator=(PackedVarying &&other);
bool isStructField() const { return !parentStructName.empty(); }
bool isStructField() const { return !frontVarying.parentStructName.empty(); }
bool isArrayElement() const { return arrayIndex != GL_INVALID_INDEX; }
std::string fullName() const
// 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;
}
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 << parentStructName << ".";
fullNameStr << varying.parentStructName << ".";
}
fullNameStr << varying->name;
fullNameStr << varying.varying->name;
if (arrayIndex != GL_INVALID_INDEX)
{
fullNameStr << "[" << arrayIndex << "]";
......@@ -63,22 +106,15 @@ struct PackedVarying : angle::NonCopyable
// Transform feedback varyings can be only referenced in the VS.
bool vertexOnly() const
{
ShaderBitSet vertex;
vertex.set(ShaderType::Vertex);
return shaderStages == vertex;
return frontVarying.stage == ShaderType::Vertex && backVarying.varying == nullptr;
}
const sh::ShaderVariable *varying;
ShaderBitSet shaderStages;
VaryingInShaderRef frontVarying;
VaryingInShaderRef backVarying;
// Cached so we can store sh::ShaderVariable to point to varying fields.
sh::InterpolationType interpolation;
// Struct name
std::string parentStructName;
std::string parentStructMappedName;
GLuint arrayIndex;
// Field index in the struct. In Vulkan, this is used to assign a
......@@ -112,14 +148,7 @@ struct PackedVaryingRegister final
std::string tfVaryingName() const
{
if (packedVarying->isArrayElement() || packedVarying->isStructField())
{
return packedVarying->fullName();
}
else
{
return packedVarying->varying->name;
}
return packedVarying->fullName(packedVarying->frontVarying.stage);
}
// Index to the array of varyings.
......@@ -182,13 +211,11 @@ class VaryingPacking final : angle::NonCopyable
return static_cast<unsigned int>(mRegisterList.size());
}
const std::vector<std::string> &getInactiveVaryingMappedNames() const
const ShaderMap<std::vector<std::string>> &getInactiveVaryingMappedNames() const
{
return mInactiveVaryingMappedNames;
}
const std::vector<sh::ShaderVariable> &getInputVaryings() const { return mInputVaryings; }
private:
bool packVarying(const PackedVarying &packedVarying);
bool isFree(unsigned int registerRow,
......@@ -199,11 +226,20 @@ class VaryingPacking final : angle::NonCopyable
unsigned int registerColumn,
const PackedVarying &packedVarying);
using VaryingUniqueFullNames = ShaderMap<std::set<std::string>>;
void packUserVarying(const ProgramVaryingRef &ref, VaryingUniqueFullNames *uniqueFullNames);
void packUserVaryingField(const ProgramVaryingRef &ref,
GLuint fieldIndex,
VaryingUniqueFullNames *uniqueFullNames);
void packUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript);
void packUserVaryingFieldTF(const ProgramVaryingRef &ref,
const sh::ShaderVariable &field,
GLuint fieldIndex);
std::vector<Register> mRegisterMap;
std::vector<PackedVaryingRegister> mRegisterList;
std::vector<sh::ShaderVariable> mInputVaryings;
std::vector<PackedVarying> mPackedVaryings;
std::vector<std::string> mInactiveVaryingMappedNames;
ShaderMap<std::vector<std::string>> mInactiveVaryingMappedNames;
PackMode mPackMode;
};
......
......@@ -30,9 +30,11 @@ class VaryingPackingTest : public ::testing::TestWithParam<GLuint>
VaryingPacking *varyingPacking)
{
std::vector<PackedVarying> packedVaryings;
for (const auto &shVarying : shVaryings)
for (const sh::ShaderVariable &shVarying : shVaryings)
{
packedVaryings.push_back(PackedVarying(shVarying, shVarying.interpolation));
packedVaryings.push_back(PackedVarying(
VaryingInShaderRef(ShaderType::Vertex, &shVarying),
VaryingInShaderRef(ShaderType::Fragment, &shVarying), shVarying.interpolation));
}
InfoLog infoLog;
......
......@@ -414,7 +414,7 @@ void DynamicHLSL::generateVaryingLinkHLSL(const VaryingPacking &varyingPacking,
for (GLuint registerIndex = 0u; registerIndex < registerInfos.size(); ++registerIndex)
{
const PackedVaryingRegister &registerInfo = registerInfos[registerIndex];
const auto &varying = *registerInfo.packedVarying->varying;
const auto &varying = registerInfo.packedVarying->varying();
ASSERT(!varying.isStruct());
// TODO: Add checks to ensure D3D interpolation modifiers don't result in too many
......@@ -599,14 +599,15 @@ void DynamicHLSL::generateShaderLinkHLSL(const gl::Caps &caps,
{
const PackedVaryingRegister &registerInfo = registerInfos[registerIndex];
const auto &packedVarying = *registerInfo.packedVarying;
const auto &varying = *packedVarying.varying;
const auto &varying = *packedVarying.frontVarying.varying;
ASSERT(!varying.isStruct());
vertexGenerateOutput << " output.v" << registerIndex << " = ";
if (packedVarying.isStructField())
{
vertexGenerateOutput << DecorateVariable(packedVarying.parentStructName) << ".";
vertexGenerateOutput << DecorateVariable(packedVarying.frontVarying.parentStructName)
<< ".";
}
vertexGenerateOutput << DecorateVariable(varying.name);
......@@ -798,19 +799,28 @@ void DynamicHLSL::generateShaderLinkHLSL(const gl::Caps &caps,
{
const PackedVaryingRegister &registerInfo = registerInfos[registerIndex];
const auto &packedVarying = *registerInfo.packedVarying;
const auto &varying = *packedVarying.varying;
// Don't reference VS-only transform feedback varyings in the PS.
if (packedVarying.vertexOnly())
{
continue;
}
const auto &varying = *packedVarying.backVarying.varying;
ASSERT(!varying.isBuiltIn() && !varying.isStruct());
// Don't reference VS-only transform feedback varyings in the PS. Note that we're relying on
// that the active flag is set according to usage in the fragment shader.
if (packedVarying.vertexOnly() || !varying.active)
// Note that we're relying on that the active flag is set according to usage in the fragment
// shader.
if (!varying.active)
{
continue;
}
pixelPrologue << " ";
if (packedVarying.isStructField())
{
pixelPrologue << DecorateVariable(packedVarying.parentStructName) << ".";
pixelPrologue << DecorateVariable(packedVarying.backVarying.parentStructName) << ".";
}
pixelPrologue << DecorateVariable(varying.name);
......
......@@ -3141,7 +3141,7 @@ void ProgramD3D::gatherTransformFeedbackVaryings(const gl::VaryingPacking &varyi
for (GLuint registerIndex = 0u; registerIndex < registerInfos.size(); ++registerIndex)
{
const auto &registerInfo = registerInfos[registerIndex];
const auto &varying = *registerInfo.packedVarying->varying;
const auto &varying = registerInfo.packedVarying->varying();
GLenum transposedType = gl::TransposeMatrixType(varying.type);
int componentCount = gl::VariableColumnCount(transposedType);
ASSERT(!varying.isBuiltIn() && !varying.isStruct());
......
......@@ -237,23 +237,20 @@ ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *inf
const std::string &varName,
uint32_t location,
uint32_t component,
gl::ShaderBitSet activeStages)
gl::ShaderType stage)
{
// The info map for this name may or may not exist already. This function merges the
// location/component information.
ShaderInterfaceVariableInfo *info = &(*infoMap)[varName];
for (const gl::ShaderType shaderType : activeStages)
{
ASSERT(info->descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->binding == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->location[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->component[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->binding == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->location[stage] == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->component[stage] == ShaderInterfaceVariableInfo::kInvalid);
info->location[shaderType] = location;
info->component[shaderType] = component;
}
info->activeStages |= activeStages;
info->location[stage] = location;
info->component[stage] = component;
info->activeStages.set(stage);
return info;
}
......@@ -489,16 +486,13 @@ void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programSt
void AssignAttributeLocations(const gl::ProgramState &programState,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
gl::ShaderBitSet vertexOnly;
vertexOnly.set(gl::ShaderType::Vertex);
// Assign attribute locations for the vertex shader.
for (const sh::ShaderVariable &attribute : programState.getProgramInputs())
{
ASSERT(attribute.active);
AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location,
ShaderInterfaceVariableInfo::kInvalid, vertexOnly);
ShaderInterfaceVariableInfo::kInvalid, gl::ShaderType::Vertex);
}
}
......@@ -512,9 +506,6 @@ void AssignOutputLocations(const gl::ProgramState &programState,
const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask",
"gl_FragStencilRefARB"};
gl::ShaderBitSet fragmentOnly;
fragmentOnly.set(gl::ShaderType::Fragment);
for (const gl::VariableLocation &outputLocation : outputLocations)
{
if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
......@@ -536,7 +527,7 @@ void AssignOutputLocations(const gl::ProgramState &programState,
}
AddLocationInfo(variableInfoMapOut, outputVar.mappedName, location,
ShaderInterfaceVariableInfo::kInvalid, fragmentOnly);
ShaderInterfaceVariableInfo::kInvalid, gl::ShaderType::Fragment);
}
}
......@@ -545,8 +536,8 @@ void AssignOutputLocations(const gl::ProgramState &programState,
// location 0 to these entries, adding an entry for them here allows us to ASSERT that every
// shader interface variable is processed during the SPIR-V transformation. This is done when
// iterating the ids provided by OpEntryPoint.
AddLocationInfo(variableInfoMapOut, "webgl_FragColor", 0, 0, fragmentOnly);
AddLocationInfo(variableInfoMapOut, "webgl_FragData", 0, 0, fragmentOnly);
AddLocationInfo(variableInfoMapOut, "webgl_FragColor", 0, 0, gl::ShaderType::Fragment);
AddLocationInfo(variableInfoMapOut, "webgl_FragData", 0, 0, gl::ShaderType::Fragment);
}
void AssignVaryingLocations(const GlslangSourceOptions &options,
......@@ -563,12 +554,12 @@ void AssignVaryingLocations(const GlslangSourceOptions &options,
{
uint32_t lineRasterEmulationPositionLocation = locationsUsedForEmulation++;
gl::ShaderBitSet allActiveStages;
allActiveStages.set();
AddLocationInfo(variableInfoMapOut, sh::vk::kLineRasterEmulationPosition,
lineRasterEmulationPositionLocation, ShaderInterfaceVariableInfo::kInvalid,
allActiveStages);
for (const gl::ShaderType shaderType : programState.getLinkedShaderStages())
{
AddLocationInfo(variableInfoMapOut, sh::vk::kLineRasterEmulationPosition,
lineRasterEmulationPositionLocation,
ShaderInterfaceVariableInfo::kInvalid, shaderType);
}
}
// Assign varying locations.
......@@ -581,6 +572,15 @@ void AssignVaryingLocations(const GlslangSourceOptions &options,
const gl::PackedVarying &varying = *varyingReg.packedVarying;
uint32_t location = varyingReg.registerRow + locationsUsedForEmulation;
uint32_t component = ShaderInterfaceVariableInfo::kInvalid;
if (varyingReg.registerColumn > 0)
{
ASSERT(!varying.varying().isStruct());
ASSERT(!gl::IsMatrixType(varying.varying().type));
component = varyingReg.registerColumn;
}
// In the following:
//
// struct S { vec4 field; };
......@@ -588,41 +588,48 @@ void AssignVaryingLocations(const GlslangSourceOptions &options,
//
// "_uvarStruct" is found through |parentStructMappedName|, with |varying->mappedName|
// being "_ufield". In such a case, use |parentStructMappedName|.
const std::string &name =
varying.isStructField() ? varying.parentStructMappedName : varying.varying->mappedName;
uint32_t location = varyingReg.registerRow + locationsUsedForEmulation;
uint32_t component = ShaderInterfaceVariableInfo::kInvalid;
if (varyingReg.registerColumn > 0)
if (varying.frontVarying.varying)
{
ASSERT(!varying.varying->isStruct());
ASSERT(!gl::IsMatrixType(varying.varying->type));
component = varyingReg.registerColumn;
const std::string &name = varying.isStructField()
? varying.frontVarying.parentStructMappedName
: varying.frontVarying.varying->mappedName;
AddLocationInfo(variableInfoMapOut, name, location, component,
varying.frontVarying.stage);
}
if (varying.backVarying.varying)
{
const std::string &name = varying.isStructField()
? varying.backVarying.parentStructMappedName
: varying.backVarying.varying->mappedName;
AddLocationInfo(variableInfoMapOut, name, location, component,
varying.backVarying.stage);
}
AddLocationInfo(variableInfoMapOut, name, location, component, varying.shaderStages);
}
// Add an entry for inactive varyings.
for (const std::string &varyingName : resources.varyingPacking.getInactiveVaryingMappedNames())
const gl::ShaderMap<std::vector<std::string>> &inactiveVaryingMappedNames =
resources.varyingPacking.getInactiveVaryingMappedNames();
for (const gl::ShaderType shaderType : programState.getLinkedShaderStages())
{
bool isBuiltin = angle::BeginsWith(varyingName, "gl_");
if (isBuiltin)
for (const std::string &varyingName : inactiveVaryingMappedNames[shaderType])
{
continue;
}
bool isBuiltin = angle::BeginsWith(varyingName, "gl_");
if (isBuiltin)
{
continue;
}
// TODO(syoussefi): inactive varying names should be unique. However, due to mishandling of
// partially captured arrays, a varying name can end up in both the active and inactive
// lists. The test below should be removed once that issue is resolved.
// http://anglebug.com/4140
if (variableInfoMapOut->find(varyingName) != variableInfoMapOut->end())
{
continue;
}
// If name is already in the map, it will automatically have marked all other stages
// inactive.
if (variableInfoMapOut->find(varyingName) != variableInfoMapOut->end())
{
continue;
}
AddLocationInfo(variableInfoMapOut, varyingName, ShaderInterfaceVariableInfo::kInvalid,
ShaderInterfaceVariableInfo::kInvalid, gl::ShaderBitSet());
// Otherwise, add an entry for it with all locations inactive.
ShaderInterfaceVariableInfo *info = &(*variableInfoMapOut)[varyingName];
ASSERT(info->location[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
}
}
}
......@@ -675,11 +682,8 @@ void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramState &programS
ASSERT(xfbVaryingLocation < locationsUsedForXfbExtension);
gl::ShaderBitSet vertexOnly;
vertexOnly.set(gl::ShaderType::Vertex);
AddLocationInfo(variableInfoMapOut, xfbVaryingName, xfbVaryingLocation,
ShaderInterfaceVariableInfo::kInvalid, vertexOnly);
ShaderInterfaceVariableInfo::kInvalid, gl::ShaderType::Vertex);
SetXfbInfo(variableInfoMapOut, xfbVaryingName, bufferIndex, currentOffset,
currentStride);
}
......@@ -703,7 +707,7 @@ void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramState &programS
const gl::PackedVarying *varying = varyingReg.packedVarying;
if (varying->varying->name == tfVarying.name)
if (varying->frontVarying.varying->name == tfVarying.name)
{
originalVarying = varying;
break;
......@@ -712,9 +716,10 @@ void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramState &programS
if (originalVarying)
{
const std::string &mappedName = originalVarying->isStructField()
? originalVarying->parentStructMappedName
: originalVarying->varying->mappedName;
const std::string &mappedName =
originalVarying->isStructField()
? originalVarying->frontVarying.parentStructMappedName
: originalVarying->frontVarying.varying->mappedName;
// Set xfb info for this varying. AssignVaryingLocations should have already added
// location information for these varyings.
......
......@@ -6166,9 +6166,6 @@ void main()
// Test that linking varyings by location works.
TEST_P(GLSLTest_ES31, LinkVaryingsByLocation)
{
// http://anglebug.com/4355
ANGLE_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsD3D11());
constexpr char kVS[] = R"(#version 310 es
precision highp float;
in vec4 position;
......
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