зеркало из https://github.com/AvaloniaUI/angle.git
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: Tim Van Patten <timvp@google.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Родитель
d8ca7d6247
Коммит
3e33db95e4
|
@ -48,7 +48,7 @@ class HashStream final : angle::NonCopyable
|
|||
std::ostringstream mStringStream;
|
||||
};
|
||||
|
||||
HashStream &operator<<(HashStream &stream, const Shader *shader)
|
||||
HashStream &operator<<(HashStream &stream, Shader *shader)
|
||||
{
|
||||
if (shader)
|
||||
{
|
||||
|
|
|
@ -1471,7 +1471,7 @@ int Program::getAttachedShadersCount() const
|
|||
return numAttachedShaders;
|
||||
}
|
||||
|
||||
const Shader *Program::getAttachedShader(ShaderType shaderType) const
|
||||
Shader *Program::getAttachedShader(ShaderType shaderType) const
|
||||
{
|
||||
ASSERT(!mLinkingState);
|
||||
return mState.getAttachedShader(shaderType);
|
||||
|
@ -1715,15 +1715,7 @@ angle::Result Program::linkImpl(const Context *context)
|
|||
InitUniformBlockLinker(mState, &resources.uniformBlockLinker);
|
||||
InitShaderStorageBlockLinker(mState, &resources.shaderStorageBlockLinker);
|
||||
|
||||
ProgramPipeline *programPipeline = context->getState().getProgramPipeline();
|
||||
if (programPipeline && programPipeline->usesShaderProgram(id()))
|
||||
{
|
||||
mergedVaryings = context->getState().getProgramPipeline()->getMergedVaryings();
|
||||
}
|
||||
else
|
||||
{
|
||||
mergedVaryings = getMergedVaryings();
|
||||
}
|
||||
mergedVaryings = GetMergedVaryingsFromShaders(*this);
|
||||
if (!linkMergedVaryings(context, mergedVaryings, &resources.varyingPacking))
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
|
@ -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)
|
||||
{
|
||||
return a.getArraySizeProduct() > b.getArraySizeProduct();
|
||||
|
@ -5795,5 +5656,4 @@ void Program::fillProgramStateMap(ShaderMap<const ProgramState *> *programStates
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
|
|
@ -470,7 +470,17 @@ struct 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:
|
||||
Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle);
|
||||
|
@ -491,7 +501,7 @@ class Program final : public LabeledObject, public angle::Subject
|
|||
void detachShader(const Context *context, Shader *shader);
|
||||
int getAttachedShadersCount() const;
|
||||
|
||||
const Shader *getAttachedShader(ShaderType shaderType) const;
|
||||
Shader *getAttachedShader(ShaderType shaderType) const override;
|
||||
|
||||
void bindAttributeLocation(GLuint index, const char *name);
|
||||
void bindUniformLocation(UniformLocation location, const char *name);
|
||||
|
@ -928,7 +938,6 @@ class Program final : public LabeledObject, public angle::Subject
|
|||
|
||||
void gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings, ShaderType stage);
|
||||
|
||||
ProgramMergedVaryings getMergedVaryings() const;
|
||||
int getOutputLocationForLink(const sh::ShaderVariable &outputVariable) const;
|
||||
bool isOutputSecondaryForLink(const sh::ShaderVariable &outputVariable) const;
|
||||
bool linkOutputVariables(const Caps &caps,
|
||||
|
|
|
@ -409,123 +409,6 @@ void ProgramPipeline::updateExecutable()
|
|||
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.
|
||||
// Uniform, input and output variables get collected.
|
||||
// The code gets compiled into binaries.
|
||||
|
@ -556,7 +439,7 @@ angle::Result ProgramPipeline::link(const Context *context)
|
|||
return angle::Result::Stop;
|
||||
}
|
||||
|
||||
mergedVaryings = getMergedVaryings();
|
||||
mergedVaryings = GetMergedVaryingsFromShaders(*this);
|
||||
for (ShaderType shaderType : getExecutable().getLinkedShaderStages())
|
||||
{
|
||||
Program *program = mState.mPrograms[shaderType];
|
||||
|
@ -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
|
||||
|
|
|
@ -91,7 +91,8 @@ class ProgramPipelineState final : angle::NonCopyable
|
|||
|
||||
class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
||||
public LabeledObject,
|
||||
public angle::ObserverInterface
|
||||
public angle::ObserverInterface,
|
||||
public HasAttachedShaders
|
||||
{
|
||||
public:
|
||||
ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle);
|
||||
|
@ -127,7 +128,6 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
|||
Program *getShaderProgram(ShaderType shaderType) const { return mState.mPrograms[shaderType]; }
|
||||
|
||||
void resetIsLinked() { mState.mIsLinked = false; }
|
||||
ProgramMergedVaryings getMergedVaryings() const;
|
||||
angle::Result link(const gl::Context *context);
|
||||
bool linkVaryings(InfoLog &infoLog) const;
|
||||
void validate(const gl::Context *context);
|
||||
|
@ -145,6 +145,8 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
|||
|
||||
void fillProgramStateMap(gl::ShaderMap<const gl::ProgramState *> *programStatesOut);
|
||||
|
||||
Shader *getAttachedShader(ShaderType shaderType) const override;
|
||||
|
||||
private:
|
||||
void updateLinkedShaderStages();
|
||||
void updateExecutableAttributes();
|
||||
|
|
|
@ -88,6 +88,27 @@ bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
|
|||
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
|
||||
|
||||
// Implementation of VaryingInShaderRef
|
||||
|
@ -812,4 +833,77 @@ bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
|
|||
|
||||
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
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
namespace gl
|
||||
{
|
||||
class HasAttachedShaders;
|
||||
class InfoLog;
|
||||
struct ProgramVaryingRef;
|
||||
|
||||
|
@ -287,6 +288,8 @@ class VaryingPacking final : angle::NonCopyable
|
|||
ShaderMap<std::vector<std::string>> mActiveOutputBuiltIns;
|
||||
};
|
||||
|
||||
// Takes an abstract handle to a program or pipeline.
|
||||
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline);
|
||||
} // namespace gl
|
||||
|
||||
#endif // LIBANGLE_VARYINGPACKING_H_
|
||||
|
|
|
@ -202,27 +202,7 @@ void ProgramPipelineTest31::drawQuadWithPPO(const std::string &positionAttribNam
|
|||
const GLfloat positionAttribZ,
|
||||
const GLfloat positionAttribXYScale)
|
||||
{
|
||||
glUseProgram(0);
|
||||
|
||||
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);
|
||||
return drawQuadPPO(mVertProg, positionAttribName, positionAttribZ, positionAttribXYScale);
|
||||
}
|
||||
|
||||
// Test glUseProgramStages
|
||||
|
@ -593,6 +573,83 @@ void main()
|
|||
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_ES31(ProgramPipelineTest31);
|
||||
|
||||
|
|
|
@ -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,
|
||||
const std::string &positionAttribName,
|
||||
GLfloat positionAttribZ)
|
||||
|
|
|
@ -395,6 +395,11 @@ class ANGLETestBase
|
|||
bool useVertexBuffer,
|
||||
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<GLushort, 6> GetQuadIndices();
|
||||
static std::array<angle::Vector3, 4> GetIndexedQuadVertices();
|
||||
|
|
Загрузка…
Ссылка в новой задаче