Commit 323c5f24 by Tim Van Patten Committed by Commit Bot

Validate PPO sampler uniforms

"Command & Conquer: Rivals" uses PPOs and was hitting the following assert: angle::Result ContextVk::updateActiveTextures(const gl::Context *context) { ... for (size_t textureUnit : activeTextures) { gl::Texture *texture = textures[textureUnit]; gl::TextureType textureType = textureTypes[textureUnit]; ASSERT(textureType != gl::TextureType::InvalidEnum); This is the same assert that is generated by the test ProgramPipelineTest31.DifferentTextureTypes which is currently being skipped since it's known to fail. This CL refactors sampler validation into the ProgramExecutable to allow PPOs to take advantage of the shared code and behave correctly, since the necessary data is already copied into the PPO's ProgramExecutable via ProgramExecutable::updateActiveSamplers(). This also takes advantage of the subject/observer pattern that's already established between programs and PPOs to ensure only the PPOs that the program is a part of are updated when a program's sampler uniforms are updated. Bug: angleproject:3570 Bug: b/182409935 Test: ProgramPipelineTest31.DifferentTextureTypes Change-Id: I3d34efd66dc85e7ff23a8422cb14d5f90a5f7085 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2792862 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent 9b0911a0
...@@ -57,6 +57,8 @@ enum class SubjectMessage ...@@ -57,6 +57,8 @@ enum class SubjectMessage
// Indicates a separable program was successfully re-linked. // Indicates a separable program was successfully re-linked.
ProgramRelinked, ProgramRelinked,
// Indicates a separable program's sampler uniforms were updated.
SamplerUniformsUpdated,
}; };
// The observing class inherits from this interface class. // The observing class inherits from this interface class.
......
...@@ -3033,34 +3033,6 @@ void Program::validate(const Caps &caps) ...@@ -3033,34 +3033,6 @@ void Program::validate(const Caps &caps)
} }
} }
bool Program::validateSamplersImpl(InfoLog *infoLog, const Caps &caps)
{
const ProgramExecutable *executable = mState.mExecutable.get();
ASSERT(!mLinkingState);
// if any two active samplers in a program are of different types, but refer to the same
// texture image unit, and this is the current program, then ValidateProgram will fail, and
// DrawArrays and DrawElements will issue the INVALID_OPERATION error.
for (size_t textureUnit : executable->mActiveSamplersMask)
{
if (executable->mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum)
{
if (infoLog)
{
(*infoLog) << "Samplers of conflicting types refer to the same texture "
"image unit ("
<< textureUnit << ").";
}
mCachedValidateSamplersResult = false;
return false;
}
}
mCachedValidateSamplersResult = true;
return true;
}
bool Program::isValidated() const bool Program::isValidated() const
{ {
ASSERT(!mLinkingState); ASSERT(!mLinkingState);
...@@ -4482,7 +4454,9 @@ void Program::updateSamplerUniform(Context *context, ...@@ -4482,7 +4454,9 @@ void Program::updateSamplerUniform(Context *context,
} }
// Invalidate the validation cache. // Invalidate the validation cache.
mCachedValidateSamplersResult.reset(); getExecutable().resetCachedValidateSamplersResult();
// Inform any PPOs this Program may be bound to.
onStateChange(angle::SubjectMessage::SamplerUniformsUpdated);
} }
void ProgramState::setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex) void ProgramState::setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex)
......
...@@ -714,23 +714,8 @@ class Program final : public LabeledObject, public angle::Subject, public HasAtt ...@@ -714,23 +714,8 @@ class Program final : public LabeledObject, public angle::Subject, public HasAtt
bool isFlaggedForDeletion() const; bool isFlaggedForDeletion() const;
void validate(const Caps &caps); void validate(const Caps &caps);
bool validateSamplers(InfoLog *infoLog, const Caps &caps)
{
// Skip cache if we're using an infolog, so we get the full error.
// Also skip the cache if the sample mapping has changed, or if we haven't ever validated.
if (infoLog == nullptr && mCachedValidateSamplersResult.valid())
{
return mCachedValidateSamplersResult.value();
}
return validateSamplersImpl(infoLog, caps);
}
bool isValidated() const; bool isValidated() const;
Optional<bool> getCachedValidateSamplersResult() { return mCachedValidateSamplersResult; }
void setCachedValidateSamplersResult(bool result) { mCachedValidateSamplersResult = result; }
const std::vector<ImageBinding> &getImageBindings() const const std::vector<ImageBinding> &getImageBindings() const
{ {
ASSERT(!mLinkingState); ASSERT(!mLinkingState);
...@@ -913,8 +898,6 @@ class Program final : public LabeledObject, public angle::Subject, public HasAtt ...@@ -913,8 +898,6 @@ class Program final : public LabeledObject, public angle::Subject, public HasAtt
GLuint getSamplerUniformBinding(const VariableLocation &uniformLocation) const; GLuint getSamplerUniformBinding(const VariableLocation &uniformLocation) const;
GLuint getImageUniformBinding(const VariableLocation &uniformLocation) const; GLuint getImageUniformBinding(const VariableLocation &uniformLocation) const;
bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps);
// Block until linking is finished and resolve it. // Block until linking is finished and resolve it.
void resolveLinkImpl(const gl::Context *context); void resolveLinkImpl(const gl::Context *context);
...@@ -941,9 +924,6 @@ class Program final : public LabeledObject, public angle::Subject, public HasAtt ...@@ -941,9 +924,6 @@ class Program final : public LabeledObject, public angle::Subject, public HasAtt
ShaderProgramManager *mResourceManager; ShaderProgramManager *mResourceManager;
const ShaderProgramID mHandle; const ShaderProgramID mHandle;
// Cache for sampler validation
Optional<bool> mCachedValidateSamplersResult;
DirtyBits mDirtyBits; DirtyBits mDirtyBits;
}; };
} // namespace gl } // namespace gl
......
...@@ -751,6 +751,7 @@ void ProgramExecutable::updateActiveSamplers(const ProgramState &programState) ...@@ -751,6 +751,7 @@ void ProgramExecutable::updateActiveSamplers(const ProgramState &programState)
{ {
if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType) if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType)
{ {
// Conflicts are marked with InvalidEnum
mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum; mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
} }
if (mActiveSamplerYUV.test(textureUnit) != if (mActiveSamplerYUV.test(textureUnit) !=
...@@ -1112,4 +1113,29 @@ void ProgramExecutable::updateTransformFeedbackStrides() ...@@ -1112,4 +1113,29 @@ void ProgramExecutable::updateTransformFeedbackStrides()
} }
} }
bool ProgramExecutable::validateSamplersImpl(InfoLog *infoLog, const Caps &caps) const
{
// if any two active samplers in a program are of different types, but refer to the same
// texture image unit, and this is the current program, then ValidateProgram will fail, and
// DrawArrays and DrawElements will issue the INVALID_OPERATION error.
for (size_t textureUnit : mActiveSamplersMask)
{
if (mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum)
{
if (infoLog)
{
(*infoLog) << "Samplers of conflicting types refer to the same texture "
"image unit ("
<< textureUnit << ").";
}
mCachedValidateSamplersResult = false;
return false;
}
}
mCachedValidateSamplersResult = true;
return true;
}
} // namespace gl } // namespace gl
...@@ -344,6 +344,20 @@ class ProgramExecutable final : public angle::Subject ...@@ -344,6 +344,20 @@ class ProgramExecutable final : public angle::Subject
GLenum getTessGenMode() const { return mTessGenMode; } GLenum getTessGenMode() const { return mTessGenMode; }
void resetCachedValidateSamplersResult() { mCachedValidateSamplersResult.reset(); }
bool validateSamplers(InfoLog *infoLog, const Caps &caps) const
{
// Use the cache if:
// - we aren't using an infolog (which gives the full error).
// - The sample mapping hasn't changed and we've already validated.
if (infoLog == nullptr && mCachedValidateSamplersResult.valid())
{
return mCachedValidateSamplersResult.value();
}
return validateSamplersImpl(infoLog, caps);
}
private: private:
// TODO(timvp): http://anglebug.com/3570: Investigate removing these friend // TODO(timvp): http://anglebug.com/3570: Investigate removing these friend
// class declarations and accessing the necessary members with getters/setters. // class declarations and accessing the necessary members with getters/setters.
...@@ -377,6 +391,8 @@ class ProgramExecutable final : public angle::Subject ...@@ -377,6 +391,8 @@ class ProgramExecutable final : public angle::Subject
void updateTransformFeedbackStrides(); void updateTransformFeedbackStrides();
bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps) const;
InfoLog mInfoLog; InfoLog mInfoLog;
ShaderBitSet mLinkedGraphicsShaderStages; ShaderBitSet mLinkedGraphicsShaderStages;
...@@ -481,6 +497,9 @@ class ProgramExecutable final : public angle::Subject ...@@ -481,6 +497,9 @@ class ProgramExecutable final : public angle::Subject
GLenum mTessGenSpacing; GLenum mTessGenSpacing;
GLenum mTessGenVertexOrder; GLenum mTessGenVertexOrder;
GLenum mTessGenPointMode; GLenum mTessGenPointMode;
// Cache for sampler validation
mutable Optional<bool> mCachedValidateSamplersResult;
}; };
} // namespace gl } // namespace gl
......
...@@ -656,20 +656,6 @@ void ProgramPipeline::validate(const gl::Context *context) ...@@ -656,20 +656,6 @@ void ProgramPipeline::validate(const gl::Context *context)
} }
} }
bool ProgramPipeline::validateSamplers(InfoLog *infoLog, const Caps &caps)
{
for (const ShaderType shaderType : gl::AllShaderTypes())
{
Program *shaderProgram = mState.mPrograms[shaderType];
if (shaderProgram && !shaderProgram->validateSamplers(infoLog, caps))
{
return false;
}
}
return true;
}
void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
{ {
switch (message) switch (message)
...@@ -682,6 +668,9 @@ void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::Sub ...@@ -682,6 +668,9 @@ void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::Sub
mState.mIsLinked = false; mState.mIsLinked = false;
updateExecutable(); updateExecutable();
break; break;
case angle::SubjectMessage::SamplerUniformsUpdated:
getExecutable().resetCachedValidateSamplersResult();
break;
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
......
...@@ -131,13 +131,6 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>, ...@@ -131,13 +131,6 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
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);
bool validateSamplers(InfoLog *infoLog, const Caps &caps);
bool usesShaderProgram(ShaderProgramID program) const
{
return mState.usesShaderProgram(program);
}
GLboolean isValid() const { return mState.isValid(); } GLboolean isValid() const { return mState.isValid(); }
// ObserverInterface implementation. // ObserverInterface implementation.
......
...@@ -3808,11 +3808,6 @@ const char *ValidateDrawStates(const Context *context) ...@@ -3808,11 +3808,6 @@ const char *ValidateDrawStates(const Context *context)
if (program) if (program)
{ {
if (!program->validateSamplers(nullptr, context->getCaps()))
{
return kTextureTypeConflict;
}
const char *errorMsg = ValidateProgramDrawStates(state, extensions, program); const char *errorMsg = ValidateProgramDrawStates(state, extensions, program);
if (errorMsg) if (errorMsg)
{ {
...@@ -3829,11 +3824,6 @@ const char *ValidateDrawStates(const Context *context) ...@@ -3829,11 +3824,6 @@ const char *ValidateDrawStates(const Context *context)
return errorMsg; return errorMsg;
} }
if (!programPipeline->validateSamplers(nullptr, context->getCaps()))
{
return kTextureTypeConflict;
}
errorMsg = ValidateProgramPipelineDrawStates(state, extensions, programPipeline); errorMsg = ValidateProgramPipelineDrawStates(state, extensions, programPipeline);
if (errorMsg) if (errorMsg)
{ {
...@@ -3855,17 +3845,25 @@ const char *ValidateDrawStates(const Context *context) ...@@ -3855,17 +3845,25 @@ const char *ValidateDrawStates(const Context *context)
programIsYUVOutput = executable->isYUVOutput(); programIsYUVOutput = executable->isYUVOutput();
} }
if (executable && executable->hasLinkedTessellationShader()) if (executable)
{ {
if (!executable->hasLinkedShaderStage(ShaderType::Vertex)) if (!executable->validateSamplers(nullptr, context->getCaps()))
{ {
return kTessellationShaderRequiresVertexShader; return kTextureTypeConflict;
} }
if (!executable->hasLinkedShaderStage(ShaderType::TessControl) || if (executable->hasLinkedTessellationShader())
!executable->hasLinkedShaderStage(ShaderType::TessEvaluation))
{ {
return kTessellationShaderRequiresBothControlAndEvaluation; if (!executable->hasLinkedShaderStage(ShaderType::Vertex))
{
return kTessellationShaderRequiresVertexShader;
}
if (!executable->hasLinkedShaderStage(ShaderType::TessControl) ||
!executable->hasLinkedShaderStage(ShaderType::TessEvaluation))
{
return kTessellationShaderRequiresBothControlAndEvaluation;
}
} }
} }
......
...@@ -459,10 +459,6 @@ TEST_P(ProgramPipelineTest31, DifferentTextureTypes) ...@@ -459,10 +459,6 @@ TEST_P(ProgramPipelineTest31, DifferentTextureTypes)
// Only the Vulkan backend supports PPO // Only the Vulkan backend supports PPO
ANGLE_SKIP_TEST_IF(!IsVulkan()); ANGLE_SKIP_TEST_IF(!IsVulkan());
// TODO (timvp): Fix this test for Vulkan with PPO
// http://anglebug.com/3570
ANGLE_SKIP_TEST_IF(IsVulkan());
// Per the OpenGL ES 3.1 spec: // Per the OpenGL ES 3.1 spec:
// //
// It is not allowed to have variables of different sampler types pointing to the same texture // It is not allowed to have variables of different sampler types pointing to the same texture
......
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