Commit 3e33db95 by Jamie Madill Committed by Commit Bot

Merge Program/ProgramPipeline::getMergedVaryings().

This merges two very similar pieces of code into one simpler function. The function doesn't use any maps or indirection to build the merged varyings list. It also fixes a potential bug with IO blocks and name matching due to the code bifurcation. Bug: angleproject:5496 Change-Id: Ibf54faeeb01d1940570b366ed153fff7c9135c52 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2606533 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent d8ca7d62
...@@ -48,7 +48,7 @@ class HashStream final : angle::NonCopyable ...@@ -48,7 +48,7 @@ class HashStream final : angle::NonCopyable
std::ostringstream mStringStream; std::ostringstream mStringStream;
}; };
HashStream &operator<<(HashStream &stream, const Shader *shader) HashStream &operator<<(HashStream &stream, Shader *shader)
{ {
if (shader) if (shader)
{ {
......
...@@ -1471,7 +1471,7 @@ int Program::getAttachedShadersCount() const ...@@ -1471,7 +1471,7 @@ int Program::getAttachedShadersCount() const
return numAttachedShaders; return numAttachedShaders;
} }
const Shader *Program::getAttachedShader(ShaderType shaderType) const Shader *Program::getAttachedShader(ShaderType shaderType) const
{ {
ASSERT(!mLinkingState); ASSERT(!mLinkingState);
return mState.getAttachedShader(shaderType); return mState.getAttachedShader(shaderType);
...@@ -1715,15 +1715,7 @@ angle::Result Program::linkImpl(const Context *context) ...@@ -1715,15 +1715,7 @@ angle::Result Program::linkImpl(const Context *context)
InitUniformBlockLinker(mState, &resources.uniformBlockLinker); InitUniformBlockLinker(mState, &resources.uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &resources.shaderStorageBlockLinker); InitShaderStorageBlockLinker(mState, &resources.shaderStorageBlockLinker);
ProgramPipeline *programPipeline = context->getState().getProgramPipeline(); mergedVaryings = GetMergedVaryingsFromShaders(*this);
if (programPipeline && programPipeline->usesShaderProgram(id()))
{
mergedVaryings = context->getState().getProgramPipeline()->getMergedVaryings();
}
else
{
mergedVaryings = getMergedVaryings();
}
if (!linkMergedVaryings(context, mergedVaryings, &resources.varyingPacking)) if (!linkMergedVaryings(context, mergedVaryings, &resources.varyingPacking))
{ {
return angle::Result::Continue; return angle::Result::Continue;
...@@ -4560,137 +4552,6 @@ void Program::gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyi ...@@ -4560,137 +4552,6 @@ 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.
//
// Note that shader I/O blocks may or may not provide a name, and matching would be done by
// block name instead if either of the shader stages doesn't provide an instance name.
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)
{
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;
ASSERT(!varying.name.empty() || varying.isShaderIOBlock);
// Map by name, or if shader I/O block, block name. Even if location is provided in
// this stage, it may not be in the paired stage.
if (varying.isShaderIOBlock)
{
outputVaryingNameToIndex[stage][varying.structName] = merged.size() - 1;
}
else
{
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)
{
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)
{
// 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())
{
ASSERT(varying.isShaderIOBlock || !varying.name.empty());
const std::string &name =
varying.isShaderIOBlock ? varying.structName : varying.name;
auto byNameIter = outputVaryingNameToIndex[previousStage].find(name);
if (byNameIter != outputVaryingNameToIndex[previousStage].end())
{
mergedIndex = byNameIter->second;
}
}
}
// If no previous stage, or not matched by location or name, create a new entry for it.
if (mergedIndex == merged.size())
{
merged.push_back({});
mergedIndex = merged.size() - 1;
}
ProgramVaryingRef *ref = &merged[mergedIndex];
ref->backShader = &varying;
ref->backShaderStage = stage;
}
}
return merged;
}
bool CompareOutputVariable(const sh::ShaderVariable &a, const sh::ShaderVariable &b) bool CompareOutputVariable(const sh::ShaderVariable &a, const sh::ShaderVariable &b)
{ {
return a.getArraySizeProduct() > b.getArraySizeProduct(); return a.getArraySizeProduct() > b.getArraySizeProduct();
...@@ -5795,5 +5656,4 @@ void Program::fillProgramStateMap(ShaderMap<const ProgramState *> *programStates ...@@ -5795,5 +5656,4 @@ void Program::fillProgramStateMap(ShaderMap<const ProgramState *> *programStates
} }
} }
} }
} // namespace gl } // namespace gl
...@@ -470,7 +470,17 @@ struct ProgramVaryingRef ...@@ -470,7 +470,17 @@ struct ProgramVaryingRef
using ProgramMergedVaryings = std::vector<ProgramVaryingRef>; using ProgramMergedVaryings = std::vector<ProgramVaryingRef>;
class Program final : public LabeledObject, public angle::Subject // TODO: Copy necessary shader state into Program. http://anglebug.com/5506
class HasAttachedShaders
{
public:
virtual Shader *getAttachedShader(ShaderType shaderType) const = 0;
protected:
virtual ~HasAttachedShaders() {}
};
class Program final : public LabeledObject, public angle::Subject, public HasAttachedShaders
{ {
public: public:
Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle); Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle);
...@@ -491,7 +501,7 @@ class Program final : public LabeledObject, public angle::Subject ...@@ -491,7 +501,7 @@ class Program final : public LabeledObject, public angle::Subject
void detachShader(const Context *context, Shader *shader); void detachShader(const Context *context, Shader *shader);
int getAttachedShadersCount() const; int getAttachedShadersCount() const;
const Shader *getAttachedShader(ShaderType shaderType) const; Shader *getAttachedShader(ShaderType shaderType) const override;
void bindAttributeLocation(GLuint index, const char *name); void bindAttributeLocation(GLuint index, const char *name);
void bindUniformLocation(UniformLocation location, const char *name); void bindUniformLocation(UniformLocation location, const char *name);
...@@ -928,7 +938,6 @@ class Program final : public LabeledObject, public angle::Subject ...@@ -928,7 +938,6 @@ class Program final : public LabeledObject, public angle::Subject
void gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings, ShaderType stage); void gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings, ShaderType stage);
ProgramMergedVaryings getMergedVaryings() const;
int getOutputLocationForLink(const sh::ShaderVariable &outputVariable) const; int getOutputLocationForLink(const sh::ShaderVariable &outputVariable) const;
bool isOutputSecondaryForLink(const sh::ShaderVariable &outputVariable) const; bool isOutputSecondaryForLink(const sh::ShaderVariable &outputVariable) const;
bool linkOutputVariables(const Caps &caps, bool linkOutputVariables(const Caps &caps,
......
...@@ -409,123 +409,6 @@ void ProgramPipeline::updateExecutable() ...@@ -409,123 +409,6 @@ void ProgramPipeline::updateExecutable()
updateHasBooleans(); updateHasBooleans();
} }
ProgramMergedVaryings ProgramPipeline::getMergedVaryings() const
{
ASSERT(!mState.mExecutable->isCompute());
// 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 shaderType : getExecutable().getLinkedShaderStages())
{
previousActiveStage[shaderType] = lastActiveStage;
const Program *program = getShaderProgram(shaderType);
ASSERT(program);
lastActiveStage = shaderType;
}
// 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>> outputVaryingNameToIndexShaderMap;
ShaderMap<std::map<int, size_t>> outputVaryingLocationToIndexShaderMap;
ProgramMergedVaryings merged;
// Gather output varyings.
for (ShaderType shaderType : getExecutable().getLinkedShaderStages())
{
const Program *program = getShaderProgram(shaderType);
ASSERT(program);
Shader *shader = program->getState().getAttachedShader(shaderType);
ASSERT(shader);
for (const sh::ShaderVariable &varying : shader->getOutputVaryings())
{
merged.push_back({});
ProgramVaryingRef *ref = &merged.back();
ref->frontShader = &varying;
ref->frontShaderStage = shaderType;
// Always map by name. Even if location is provided in this stage, it may not be in the
// paired stage.
outputVaryingNameToIndexShaderMap[shaderType][varying.name] = merged.size() - 1;
// If location is provided, also keep it in a map by location.
if (varying.location != -1)
{
outputVaryingLocationToIndexShaderMap[shaderType][varying.location] =
merged.size() - 1;
}
}
}
// Gather input varyings, and match them with output varyings of the previous stage.
for (ShaderType shaderType : getExecutable().getLinkedShaderStages())
{
const Program *program = getShaderProgram(shaderType);
ASSERT(program);
Shader *shader = program->getState().getAttachedShader(shaderType);
ASSERT(shader);
ShaderType previousStage = previousActiveStage[shaderType];
for (const sh::ShaderVariable &varying : shader->getInputVaryings())
{
size_t mergedIndex = merged.size();
if (previousStage != ShaderType::InvalidEnum)
{
// If location is provided, see if we can match by location.
if (varying.location != -1)
{
std::map<int, size_t> outputVaryingLocationToIndex =
outputVaryingLocationToIndexShaderMap[previousStage];
auto byLocationIter = outputVaryingLocationToIndex.find(varying.location);
if (byLocationIter != outputVaryingLocationToIndex.end())
{
mergedIndex = byLocationIter->second;
}
}
// If not found, try to match by name.
if (mergedIndex == merged.size())
{
std::map<std::string, size_t> outputVaryingNameToIndex =
outputVaryingNameToIndexShaderMap[previousStage];
auto byNameIter = outputVaryingNameToIndex.find(varying.name);
if (byNameIter != outputVaryingNameToIndex.end())
{
mergedIndex = byNameIter->second;
}
}
}
// If no previous stage, or not matched by location or name, create a new entry for it.
if (mergedIndex == merged.size())
{
merged.push_back({});
mergedIndex = merged.size() - 1;
}
ProgramVaryingRef *ref = &merged[mergedIndex];
ref->backShader = &varying;
ref->backShaderStage = shaderType;
}
}
return merged;
}
// 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.
...@@ -556,7 +439,7 @@ angle::Result ProgramPipeline::link(const Context *context) ...@@ -556,7 +439,7 @@ angle::Result ProgramPipeline::link(const Context *context)
return angle::Result::Stop; return angle::Result::Stop;
} }
mergedVaryings = getMergedVaryings(); mergedVaryings = GetMergedVaryingsFromShaders(*this);
for (ShaderType shaderType : getExecutable().getLinkedShaderStages()) for (ShaderType shaderType : getExecutable().getLinkedShaderStages())
{ {
Program *program = mState.mPrograms[shaderType]; Program *program = mState.mPrograms[shaderType];
...@@ -707,4 +590,9 @@ void ProgramPipeline::fillProgramStateMap(ShaderMap<const ProgramState *> *progr ...@@ -707,4 +590,9 @@ void ProgramPipeline::fillProgramStateMap(ShaderMap<const ProgramState *> *progr
} }
} }
Shader *ProgramPipeline::getAttachedShader(ShaderType shaderType) const
{
const Program *program = mState.mPrograms[shaderType];
return program ? program->getAttachedShader(shaderType) : nullptr;
}
} // namespace gl } // namespace gl
...@@ -91,7 +91,8 @@ class ProgramPipelineState final : angle::NonCopyable ...@@ -91,7 +91,8 @@ class ProgramPipelineState final : angle::NonCopyable
class ProgramPipeline final : public RefCountObject<ProgramPipelineID>, class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
public LabeledObject, public LabeledObject,
public angle::ObserverInterface public angle::ObserverInterface,
public HasAttachedShaders
{ {
public: public:
ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle); ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle);
...@@ -127,7 +128,6 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>, ...@@ -127,7 +128,6 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
Program *getShaderProgram(ShaderType shaderType) const { return mState.mPrograms[shaderType]; } Program *getShaderProgram(ShaderType shaderType) const { return mState.mPrograms[shaderType]; }
void resetIsLinked() { mState.mIsLinked = false; } void resetIsLinked() { mState.mIsLinked = false; }
ProgramMergedVaryings getMergedVaryings() const;
angle::Result link(const gl::Context *context); angle::Result link(const gl::Context *context);
bool linkVaryings(InfoLog &infoLog) const; bool linkVaryings(InfoLog &infoLog) const;
void validate(const gl::Context *context); void validate(const gl::Context *context);
...@@ -145,6 +145,8 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>, ...@@ -145,6 +145,8 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
void fillProgramStateMap(gl::ShaderMap<const gl::ProgramState *> *programStatesOut); void fillProgramStateMap(gl::ShaderMap<const gl::ProgramState *> *programStatesOut);
Shader *getAttachedShader(ShaderType shaderType) const override;
private: private:
void updateLinkedShaderStages(); void updateLinkedShaderStages();
void updateExecutableAttributes(); void updateExecutableAttributes();
......
...@@ -88,6 +88,27 @@ bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y) ...@@ -88,6 +88,27 @@ bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
return gl::CompareShaderVar(*px, *py); return gl::CompareShaderVar(*px, *py);
} }
bool InterfaceVariablesMatch(const sh::ShaderVariable &front, const sh::ShaderVariable &back)
{
// Matching ruels from 7.4.1 Shader Interface Matching from the GLES 3.2 spec:
// - 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. Note that we use a more permissive check here thanks to front-end validation.
if (back.location != -1 && back.location == front.location)
{
return true;
}
if (front.isShaderIOBlock != back.isShaderIOBlock)
{
return false;
}
// Compare names, or if shader I/O blocks, block names.
const std::string &backName = back.isShaderIOBlock ? back.structName : back.name;
const std::string &frontName = front.isShaderIOBlock ? front.structName : front.name;
return backName == frontName;
}
} // anonymous namespace } // anonymous namespace
// Implementation of VaryingInShaderRef // Implementation of VaryingInShaderRef
...@@ -812,4 +833,77 @@ bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog, ...@@ -812,4 +833,77 @@ bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
return true; return true;
} }
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline)
{
Shader *frontShader = nullptr;
ProgramMergedVaryings merged;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
Shader *backShader = programOrPipeline.getAttachedShader(shaderType);
if (!backShader)
{
continue;
}
ASSERT(backShader->getType() != ShaderType::Compute);
// Add outputs. These are always unmatched since we walk shader stages sequentially.
for (const sh::ShaderVariable &frontVarying : backShader->getOutputVaryings())
{
ProgramVaryingRef ref;
ref.frontShader = &frontVarying;
ref.frontShaderStage = backShader->getType();
merged.push_back(ref);
}
if (!frontShader)
{
// If this is our first shader stage, and not a VS, we might have unmatched inputs.
for (const sh::ShaderVariable &backVarying : backShader->getInputVaryings())
{
ProgramVaryingRef ref;
ref.backShader = &backVarying;
ref.backShaderStage = backShader->getType();
merged.push_back(ref);
}
}
else
{
// Match inputs with the prior shader stage outputs.
for (const sh::ShaderVariable &backVarying : backShader->getInputVaryings())
{
bool found = false;
for (ProgramVaryingRef &ref : merged)
{
if (ref.frontShader && ref.frontShaderStage == frontShader->getType() &&
InterfaceVariablesMatch(*ref.frontShader, backVarying))
{
ASSERT(ref.backShader == nullptr);
ref.backShader = &backVarying;
ref.backShaderStage = backShader->getType();
found = true;
break;
}
}
// Some outputs are never matched, e.g. some builtin variables.
if (!found)
{
ProgramVaryingRef ref;
ref.backShader = &backVarying;
ref.backShaderStage = backShader->getType();
merged.push_back(ref);
}
}
}
// Save the current back shader to use as the next front shader.
frontShader = backShader;
}
return merged;
}
} // namespace gl } // namespace gl
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
namespace gl namespace gl
{ {
class HasAttachedShaders;
class InfoLog; class InfoLog;
struct ProgramVaryingRef; struct ProgramVaryingRef;
...@@ -287,6 +288,8 @@ class VaryingPacking final : angle::NonCopyable ...@@ -287,6 +288,8 @@ class VaryingPacking final : angle::NonCopyable
ShaderMap<std::vector<std::string>> mActiveOutputBuiltIns; ShaderMap<std::vector<std::string>> mActiveOutputBuiltIns;
}; };
// Takes an abstract handle to a program or pipeline.
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline);
} // namespace gl } // namespace gl
#endif // LIBANGLE_VARYINGPACKING_H_ #endif // LIBANGLE_VARYINGPACKING_H_
...@@ -202,27 +202,7 @@ void ProgramPipelineTest31::drawQuadWithPPO(const std::string &positionAttribNam ...@@ -202,27 +202,7 @@ void ProgramPipelineTest31::drawQuadWithPPO(const std::string &positionAttribNam
const GLfloat positionAttribZ, const GLfloat positionAttribZ,
const GLfloat positionAttribXYScale) const GLfloat positionAttribXYScale)
{ {
glUseProgram(0); return drawQuadPPO(mVertProg, positionAttribName, positionAttribZ, positionAttribXYScale);
std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.x() *= positionAttribXYScale;
vertex.y() *= positionAttribXYScale;
vertex.z() = positionAttribZ;
}
GLint positionLocation = glGetAttribLocation(mVertProg, positionAttribName.c_str());
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
} }
// Test glUseProgramStages // Test glUseProgramStages
...@@ -593,6 +573,83 @@ void main() ...@@ -593,6 +573,83 @@ void main()
ASSERT_GL_ERROR(GL_INVALID_OPERATION); ASSERT_GL_ERROR(GL_INVALID_OPERATION);
} }
// Tests creating two program pipelines with a common shader and a varying location mismatch.
TEST_P(ProgramPipelineTest31, VaryingLocationMismatch)
{
// Only the Vulkan backend supports PPOs
ANGLE_SKIP_TEST_IF(!IsVulkan());
// http://anglebug.com/5506
ANGLE_SKIP_TEST_IF(IsVulkan());
// Create a fragment shader using the varying location "5".
const char *kFS = R"(#version 310 es
precision mediump float;
layout(location = 5) in vec4 color;
out vec4 colorOut;
void main()
{
colorOut = color;
})";
// Create a pipeline with a vertex shader using varying location "5". Should succeed.
const char *kVSGood = R"(#version 310 es
precision mediump float;
layout(location = 5) out vec4 color;
in vec4 position;
uniform float uniOne;
void main()
{
gl_Position = position;
color = vec4(0, uniOne, 0, 1);
})";
mVertProg = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &kVSGood);
ASSERT_NE(mVertProg, 0u);
mFragProg = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &kFS);
ASSERT_NE(mFragProg, 0u);
// Generate a program pipeline and attach the programs to their respective stages
glGenProgramPipelines(1, &mPipeline);
glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
glBindProgramPipeline(mPipeline);
ASSERT_GL_NO_ERROR();
GLint location = glGetUniformLocation(mVertProg, "uniOne");
ASSERT_NE(-1, location);
glActiveShaderProgram(mPipeline, mVertProg);
glUniform1f(location, 1.0);
ASSERT_GL_NO_ERROR();
drawQuadWithPPO("position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Create a pipeline with a vertex shader using varying location "3". Should fail.
const char *kVSBad = R"(#version 310 es
precision mediump float;
layout(location = 3) out vec4 color;
in vec4 position;
uniform float uniOne;
void main()
{
gl_Position = position;
color = vec4(0, uniOne, 0, 1);
})";
glDeleteProgram(mVertProg);
mVertProg = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &kVSBad);
ASSERT_NE(mVertProg, 0u);
glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
ASSERT_GL_NO_ERROR();
drawQuadWithPPO("position", 0.5f, 1.0f);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ProgramPipelineTest); ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ProgramPipelineTest);
ANGLE_INSTANTIATE_TEST_ES31(ProgramPipelineTest31); ANGLE_INSTANTIATE_TEST_ES31(ProgramPipelineTest31);
......
...@@ -856,6 +856,34 @@ void ANGLETestBase::drawQuad(GLuint program, ...@@ -856,6 +856,34 @@ void ANGLETestBase::drawQuad(GLuint program,
} }
} }
void ANGLETestBase::drawQuadPPO(GLuint vertProgram,
const std::string &positionAttribName,
const GLfloat positionAttribZ,
const GLfloat positionAttribXYScale)
{
glUseProgram(0);
std::array<Vector3, 6> quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.x() *= positionAttribXYScale;
vertex.y() *= positionAttribXYScale;
vertex.z() = positionAttribZ;
}
GLint positionLocation = glGetAttribLocation(vertProgram, positionAttribName.c_str());
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
}
void ANGLETestBase::drawIndexedQuad(GLuint program, void ANGLETestBase::drawIndexedQuad(GLuint program,
const std::string &positionAttribName, const std::string &positionAttribName,
GLfloat positionAttribZ) GLfloat positionAttribZ)
......
...@@ -395,6 +395,11 @@ class ANGLETestBase ...@@ -395,6 +395,11 @@ class ANGLETestBase
bool useVertexBuffer, bool useVertexBuffer,
GLuint numInstances); GLuint numInstances);
void drawQuadPPO(GLuint vertProgram,
const std::string &positionAttribName,
const GLfloat positionAttribZ,
const GLfloat positionAttribXYScale);
static std::array<angle::Vector3, 6> GetQuadVertices(); static std::array<angle::Vector3, 6> GetQuadVertices();
static std::array<GLushort, 6> GetQuadIndices(); static std::array<GLushort, 6> GetQuadIndices();
static std::array<angle::Vector3, 4> GetIndexedQuadVertices(); static std::array<angle::Vector3, 4> GetIndexedQuadVertices();
......
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