Commit 1b2dd6f9 by Tim Van Patten Committed by Commit Bot

Vulkan: Refactor varying validation for separable programs

The varying validation is being refactored to better handle separable programs. Some additional validation is being added as well regarding: - Number of varyings (inputs vs outputs) - Precision This refactor will also allow ProgramPipelines to validate varyings between shaders that are in different Programs. Bug: angleproject:3570 Change-Id: I13f324da2ffea039e696962d6971a54f7a7b6f77 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2083847 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com>
parent b387ce93
...@@ -1392,6 +1392,33 @@ void Program::bindFragmentOutputIndex(GLuint index, const char *name) ...@@ -1392,6 +1392,33 @@ void Program::bindFragmentOutputIndex(GLuint index, const char *name)
mFragmentOutputIndexes.bindLocation(index, name); mFragmentOutputIndexes.bindLocation(index, name);
} }
angle::Result Program::linkMergedVaryings(const Context *context,
VaryingPacking &varyingPacking,
const ProgramMergedVaryings &mergedVaryings,
ProgramLinkedResources *resources)
{
ShaderType tfStage =
mState.mAttachedShaders[ShaderType::Geometry] ? ShaderType::Geometry : ShaderType::Vertex;
InfoLog &infoLog = getExecutable().getInfoLog();
if (!linkValidateTransformFeedback(context->getClientVersion(), infoLog, mergedVaryings,
tfStage, context->getCaps()))
{
return angle::Result::Stop;
}
if (!resources->varyingPacking.collectAndPackUserVaryings(
infoLog, mergedVaryings, mState.getTransformFeedbackVaryingNames(), isSeparable()))
{
return angle::Result::Stop;
}
gatherTransformFeedbackVaryings(mergedVaryings, tfStage);
mState.updateTransformFeedbackStrides();
return angle::Result::Continue;
}
// The attached shaders are checked for linking errors by matching up their variables. // The attached shaders are checked for linking errors by matching up their variables.
// Uniform, input and output variables get collected. // Uniform, input and output variables get collected.
// The code gets compiled into binaries. // The code gets compiled into binaries.
...@@ -1540,8 +1567,6 @@ angle::Result Program::link(const Context *context) ...@@ -1540,8 +1567,6 @@ angle::Result Program::link(const Context *context)
return angle::Result::Continue; return angle::Result::Continue;
} }
const ProgramMergedVaryings &mergedVaryings = getMergedVaryings();
gl::Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex]; gl::Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
if (vertexShader) if (vertexShader)
{ {
...@@ -1551,23 +1576,9 @@ angle::Result Program::link(const Context *context) ...@@ -1551,23 +1576,9 @@ angle::Result Program::link(const Context *context)
InitUniformBlockLinker(mState, &resources->uniformBlockLinker); InitUniformBlockLinker(mState, &resources->uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &resources->shaderStorageBlockLinker); InitShaderStorageBlockLinker(mState, &resources->shaderStorageBlockLinker);
ShaderType tfStage = mState.mAttachedShaders[ShaderType::Geometry] ? ShaderType::Geometry const ProgramMergedVaryings &mergedVaryings = getMergedVaryings();
: ShaderType::Vertex; ANGLE_TRY(linkMergedVaryings(context, resources->varyingPacking, mergedVaryings,
resources.get()));
if (!linkValidateTransformFeedback(context->getClientVersion(), infoLog, mergedVaryings,
tfStage, context->getCaps()))
{
return angle::Result::Continue;
}
if (!resources->varyingPacking.collectAndPackUserVaryings(
infoLog, mergedVaryings, mState.getTransformFeedbackVaryingNames(), isSeparable()))
{
return angle::Result::Continue;
}
gatherTransformFeedbackVaryings(mergedVaryings, tfStage);
mState.updateTransformFeedbackStrides();
} }
updateLinkedShaderStages(); updateLinkedShaderStages();
...@@ -3394,7 +3405,8 @@ bool Program::linkVaryings(InfoLog &infoLog) const ...@@ -3394,7 +3405,8 @@ bool Program::linkVaryings(InfoLog &infoLog) const
if (previousShader) if (previousShader)
{ {
if (!linkValidateShaderInterfaceMatching(previousShader, currentShader, infoLog)) if (!linkValidateShaderInterfaceMatching(previousShader, currentShader, isSeparable(),
infoLog))
{ {
return false; return false;
} }
...@@ -3402,7 +3414,9 @@ bool Program::linkVaryings(InfoLog &infoLog) const ...@@ -3402,7 +3414,9 @@ bool Program::linkVaryings(InfoLog &infoLog) const
previousShader = currentShader; previousShader = currentShader;
} }
if (!linkValidateBuiltInVaryings(infoLog)) Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
if (!linkValidateBuiltInVaryings(vertexShader, fragmentShader, infoLog))
{ {
return false; return false;
} }
...@@ -3410,56 +3424,101 @@ bool Program::linkVaryings(InfoLog &infoLog) const ...@@ -3410,56 +3424,101 @@ bool Program::linkVaryings(InfoLog &infoLog) const
return true; return true;
} }
void Program::getFilteredVaryings(const std::vector<sh::ShaderVariable> &varyings,
std::vector<const sh::ShaderVariable *> *filteredVaryingsOut)
{
for (const sh::ShaderVariable &varying : varyings)
{
// Built-in varyings obey special rules
if (varying.isBuiltIn())
{
continue;
}
filteredVaryingsOut->push_back(&varying);
}
}
bool Program::doShaderVariablesMatch(gl::Shader *generatingShader,
gl::Shader *consumingShader,
const sh::ShaderVariable &input,
const sh::ShaderVariable &output,
bool validateGeometryShaderInputs,
bool isSeparable,
gl::InfoLog &infoLog)
{
bool namesMatch = input.name == output.name;
bool locationsMatch = (input.location != -1) && (input.location == output.location);
// An output variable is considered to match an input variable in the subsequent
// shader if:
// - the two variables match in name, type, and qualification; or
// - the two variables are declared with the same location qualifier and
// match in type and qualification.
if (namesMatch || locationsMatch)
{
std::string mismatchedStructFieldName;
LinkMismatchError linkError = LinkValidateVaryings(
output, input, generatingShader->getShaderVersion(), validateGeometryShaderInputs,
isSeparable, &mismatchedStructFieldName);
if (linkError != LinkMismatchError::NO_MISMATCH)
{
LogLinkMismatch(infoLog, input.name, "varying", linkError, mismatchedStructFieldName,
generatingShader->getType(), consumingShader->getType());
return false;
}
return true;
}
return false;
}
// [OpenGL ES 3.1] Chapter 7.4.1 "Shader Interface Matching" Page 91 // [OpenGL ES 3.1] Chapter 7.4.1 "Shader Interface Matching" Page 91
// TODO(jiawei.shao@intel.com): add validation on input/output blocks matching // TODO(jiawei.shao@intel.com): add validation on input/output blocks matching
bool Program::linkValidateShaderInterfaceMatching(gl::Shader *generatingShader, bool Program::linkValidateShaderInterfaceMatching(gl::Shader *generatingShader,
gl::Shader *consumingShader, gl::Shader *consumingShader,
gl::InfoLog &infoLog) const bool isSeparable,
gl::InfoLog &infoLog)
{ {
ASSERT(generatingShader->getShaderVersion() == consumingShader->getShaderVersion()); ASSERT(generatingShader->getShaderVersion() == consumingShader->getShaderVersion());
const std::vector<sh::ShaderVariable> &outputVaryings = generatingShader->getOutputVaryings(); const std::vector<sh::ShaderVariable> &outputVaryings = generatingShader->getOutputVaryings();
const std::vector<sh::ShaderVariable> &inputVaryings = consumingShader->getInputVaryings(); const std::vector<sh::ShaderVariable> &inputVaryings = consumingShader->getInputVaryings();
std::vector<const sh::ShaderVariable *> filteredInputVaryings;
std::vector<const sh::ShaderVariable *> filteredOutputVaryings;
bool validateGeometryShaderInputs = consumingShader->getType() == ShaderType::Geometry; bool validateGeometryShaderInputs = consumingShader->getType() == ShaderType::Geometry;
for (const sh::ShaderVariable &input : inputVaryings) getFilteredVaryings(inputVaryings, &filteredInputVaryings);
{ getFilteredVaryings(outputVaryings, &filteredOutputVaryings);
bool matched = false;
// Built-in varyings obey special rules // Separable programs require the number of inputs and outputs match
if (input.isBuiltIn()) if (isSeparable && filteredInputVaryings.size() < filteredOutputVaryings.size())
{ {
continue; infoLog << GetShaderTypeString(consumingShader->getType())
} << " does not consume all varyings generated by "
<< GetShaderTypeString(generatingShader->getType());
return false;
}
if (isSeparable && filteredInputVaryings.size() > filteredOutputVaryings.size())
{
infoLog << GetShaderTypeString(generatingShader->getType())
<< " does not generate all varyings consumed by "
<< GetShaderTypeString(consumingShader->getType());
return false;
}
// An output variable is considered to match an input variable in the subsequent // All inputs must match all outputs
// shader if: for (const sh::ShaderVariable *input : filteredInputVaryings)
// - the two variables match in name, type, and qualification; or {
// - the two variables are declared with the same location qualifier and bool match = false;
// match in type and qualification. for (const sh::ShaderVariable *output : filteredOutputVaryings)
for (const sh::ShaderVariable &output : outputVaryings)
{ {
bool namesMatch = input.name == output.name; if (doShaderVariablesMatch(generatingShader, consumingShader, *input, *output,
bool locationsMatch = (input.location != -1) && (input.location == output.location); validateGeometryShaderInputs, isSeparable, infoLog))
if (namesMatch || locationsMatch)
{ {
ASSERT(!output.isBuiltIn()); match = true;
std::string mismatchedStructFieldName;
LinkMismatchError linkError =
LinkValidateVaryings(output, input, generatingShader->getShaderVersion(),
validateGeometryShaderInputs, &mismatchedStructFieldName);
if (linkError != LinkMismatchError::NO_MISMATCH)
{
LogLinkMismatch(infoLog, input.name, "varying", linkError,
mismatchedStructFieldName, generatingShader->getType(),
consumingShader->getType());
return false;
}
matched = true;
break; break;
} }
} }
...@@ -3467,17 +3526,15 @@ bool Program::linkValidateShaderInterfaceMatching(gl::Shader *generatingShader, ...@@ -3467,17 +3526,15 @@ bool Program::linkValidateShaderInterfaceMatching(gl::Shader *generatingShader,
// We permit unmatched, unreferenced varyings. Note that this specifically depends on // We permit unmatched, unreferenced varyings. Note that this specifically depends on
// whether the input is statically used - a statically used input should fail this test even // whether the input is statically used - a statically used input should fail this test even
// if it is not active. GLSL ES 3.00.6 section 4.3.10. // if it is not active. GLSL ES 3.00.6 section 4.3.10.
if (!matched && input.staticUse) if (!match && input->staticUse)
{ {
infoLog << GetShaderTypeString(consumingShader->getType()) << " varying " << input.name infoLog << GetShaderTypeString(consumingShader->getType()) << " varying " << input->name
<< " does not match any " << GetShaderTypeString(generatingShader->getType()) << " does not match any " << GetShaderTypeString(generatingShader->getType())
<< " varying"; << " varying";
return false; return false;
} }
} }
// TODO(jmadill): verify no unmatched output varyings?
return true; return true;
} }
...@@ -3952,6 +4009,7 @@ LinkMismatchError Program::LinkValidateVaryings(const sh::ShaderVariable &output ...@@ -3952,6 +4009,7 @@ LinkMismatchError Program::LinkValidateVaryings(const sh::ShaderVariable &output
const sh::ShaderVariable &inputVarying, const sh::ShaderVariable &inputVarying,
int shaderVersion, int shaderVersion,
bool validateGeometryShaderInputVarying, bool validateGeometryShaderInputVarying,
bool isSeparable,
std::string *mismatchedStructFieldName) std::string *mismatchedStructFieldName)
{ {
if (validateGeometryShaderInputVarying) if (validateGeometryShaderInputVarying)
...@@ -3976,14 +4034,22 @@ LinkMismatchError Program::LinkValidateVaryings(const sh::ShaderVariable &output ...@@ -3976,14 +4034,22 @@ LinkMismatchError Program::LinkValidateVaryings(const sh::ShaderVariable &output
// Skip the validation on the array sizes between a vertex output varying and a geometry input // Skip the validation on the array sizes between a vertex output varying and a geometry input
// varying as it has been done before. // varying as it has been done before.
bool validatePrecision = isSeparable && (shaderVersion > 100);
LinkMismatchError linkError = LinkMismatchError linkError =
LinkValidateVariablesBase(outputVarying, inputVarying, false, LinkValidateVariablesBase(outputVarying, inputVarying, validatePrecision,
!validateGeometryShaderInputVarying, mismatchedStructFieldName); !validateGeometryShaderInputVarying, mismatchedStructFieldName);
if (linkError != LinkMismatchError::NO_MISMATCH) if (linkError != LinkMismatchError::NO_MISMATCH)
{ {
return linkError; return linkError;
} }
// Explicit locations must match if the names match.
if ((outputVarying.name == inputVarying.name) &&
(outputVarying.location != inputVarying.location))
{
return LinkMismatchError::LOCATION_MISMATCH;
}
if (!sh::InterpolationTypesMatch(outputVarying.interpolation, inputVarying.interpolation)) if (!sh::InterpolationTypesMatch(outputVarying.interpolation, inputVarying.interpolation))
{ {
return LinkMismatchError::INTERPOLATION_TYPE_MISMATCH; return LinkMismatchError::INTERPOLATION_TYPE_MISMATCH;
...@@ -3997,11 +4063,10 @@ LinkMismatchError Program::LinkValidateVaryings(const sh::ShaderVariable &output ...@@ -3997,11 +4063,10 @@ LinkMismatchError Program::LinkValidateVaryings(const sh::ShaderVariable &output
return LinkMismatchError::NO_MISMATCH; return LinkMismatchError::NO_MISMATCH;
} }
bool Program::linkValidateBuiltInVaryings(InfoLog &infoLog) const bool Program::linkValidateBuiltInVaryings(Shader *vertexShader,
Shader *fragmentShader,
InfoLog &infoLog)
{ {
Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
if (!vertexShader || !fragmentShader) if (!vertexShader || !fragmentShader)
{ {
// We can't validate an interface if we don't have both a producer and a consumer // We can't validate an interface if we don't have both a producer and a consumer
......
...@@ -503,6 +503,11 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -503,6 +503,11 @@ class Program final : angle::NonCopyable, public LabeledObject
void bindFragmentOutputLocation(GLuint index, const char *name); void bindFragmentOutputLocation(GLuint index, const char *name);
void bindFragmentOutputIndex(GLuint index, const char *name); void bindFragmentOutputIndex(GLuint index, const char *name);
angle::Result linkMergedVaryings(const Context *context,
VaryingPacking &varyingPacking,
const ProgramMergedVaryings &mergedVaryings,
ProgramLinkedResources *resources);
// KHR_parallel_shader_compile // KHR_parallel_shader_compile
// Try to link the program asynchrously. As a result, background threads may be launched to // Try to link the program asynchrously. As a result, background threads may be launched to
// execute the linking tasks concurrently. // execute the linking tasks concurrently.
...@@ -842,6 +847,26 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -842,6 +847,26 @@ class Program final : angle::NonCopyable, public LabeledObject
const ProgramExecutable &getExecutable() const { return mState.getProgramExecutable(); } const ProgramExecutable &getExecutable() const { return mState.getProgramExecutable(); }
ProgramExecutable &getExecutable() { return mState.getProgramExecutable(); } ProgramExecutable &getExecutable() { return mState.getProgramExecutable(); }
static void getFilteredVaryings(const std::vector<sh::ShaderVariable> &varyings,
std::vector<const sh::ShaderVariable *> *filteredVaryingsOut);
static bool doShaderVariablesMatch(gl::Shader *generatingShader,
gl::Shader *consumingShader,
const sh::ShaderVariable &input,
const sh::ShaderVariable &output,
bool validateGeometryShaderInputs,
bool isSeparable,
gl::InfoLog &infoLog);
static bool linkValidateShaderInterfaceMatching(Shader *generatingShader,
Shader *consumingShader,
bool isSeparable,
InfoLog &infoLog);
static bool linkValidateBuiltInVaryings(Shader *vertexShader,
Shader *fragmentShader,
InfoLog &infoLog);
// Check for aliased path rendering input bindings (if any).
// If more than one binding refer statically to the same location the link must fail.
bool linkValidateFragmentInputBindings(InfoLog &infoLog) const;
private: private:
struct LinkingState; struct LinkingState;
...@@ -877,13 +902,9 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -877,13 +902,9 @@ class Program final : angle::NonCopyable, public LabeledObject
const sh::ShaderVariable &inputVarying, const sh::ShaderVariable &inputVarying,
int shaderVersion, int shaderVersion,
bool validateGeometryShaderInputVarying, bool validateGeometryShaderInputVarying,
bool isSeparable,
std::string *mismatchedStructFieldName); std::string *mismatchedStructFieldName);
bool linkValidateShaderInterfaceMatching(Shader *generatingShader,
Shader *consumingShader,
InfoLog &infoLog) const;
bool linkValidateBuiltInVaryings(InfoLog &infoLog) const;
bool linkValidateTransformFeedback(const Version &version, bool linkValidateTransformFeedback(const Version &version,
InfoLog &infoLog, InfoLog &infoLog,
const ProgramMergedVaryings &linkedVaryings, const ProgramMergedVaryings &linkedVaryings,
......
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