Commit 44a6fbfd by Jamie Madill Committed by Commit Bot

Optimize resolveLink.

This changes the program query to resolve the link if required. If the validation layer is skipped the link is resolved in the gl::Context. Resolving the link on program query allows us to avoid resolving the link on most of the gl::Program query APIs. This improves inlining and particularly affects uniform update. It fixes a performance regression introduced by the parallel shader linking extension. Gives a 17% increased score on a uniform benchmark. Also fixes two missing cases of checking for the extension in our validation code. Note that some bugs might still exist when the validation layer is disabled. Bug: angleproject:2851 Change-Id: I5d725eede3fa147cedf2ce0129791b3412a97a61 Reviewed-on: https://chromium-review.googlesource.com/1255509 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@google.com>
parent df836ff8
...@@ -1585,7 +1585,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1585,7 +1585,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
bool getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams); bool getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams);
bool getIndexedQueryParameterInfo(GLenum target, GLenum *type, unsigned int *numParams); bool getIndexedQueryParameterInfo(GLenum target, GLenum *type, unsigned int *numParams);
Program *getProgram(GLuint handle) const; Program *getProgramResolveLink(GLuint handle) const;
Program *getProgramNoResolveLink(GLuint handle) const;
Shader *getShader(GLuint handle) const; Shader *getShader(GLuint handle) const;
bool isTextureGenerated(GLuint texture) const; bool isTextureGenerated(GLuint texture) const;
......
...@@ -1859,7 +1859,7 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject ...@@ -1859,7 +1859,7 @@ FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::Subject
bool Framebuffer::formsRenderingFeedbackLoopWith(const State &state) const bool Framebuffer::formsRenderingFeedbackLoopWith(const State &state) const
{ {
const Program *program = state.getProgram(); const Program *program = state.getLinkedProgram();
// TODO(jmadill): Default framebuffer feedback loops. // TODO(jmadill): Default framebuffer feedback loops.
if (mState.mId == 0) if (mState.mId == 0)
......
...@@ -538,6 +538,7 @@ Error GLES1Renderer::linkProgram(Context *context, ...@@ -538,6 +538,7 @@ Error GLES1Renderer::linkProgram(Context *context,
} }
ANGLE_TRY(programObject->link(context)); ANGLE_TRY(programObject->link(context));
programObject->resolveLink();
glState->onProgramExecutableChange(programObject); glState->onProgramExecutableChange(programObject);
......
...@@ -512,7 +512,11 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -512,7 +512,11 @@ class Program final : angle::NonCopyable, public LabeledObject
// Peek whether there is any running linking tasks. // Peek whether there is any running linking tasks.
bool isLinking() const; bool isLinking() const;
bool isLinked() const; bool isLinked() const
{
ASSERT(mLinkResolved);
return mLinked;
}
bool hasLinkedShaderStage(ShaderType shaderType) const bool hasLinkedShaderStage(ShaderType shaderType) const
{ {
...@@ -570,8 +574,19 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -570,8 +574,19 @@ class Program final : angle::NonCopyable, public LabeledObject
bool isValidUniformLocation(GLint location) const; bool isValidUniformLocation(GLint location) const;
const LinkedUniform &getUniformByLocation(GLint location) const; const LinkedUniform &getUniformByLocation(GLint location) const;
const VariableLocation &getUniformLocation(GLint location) const; const VariableLocation &getUniformLocation(GLint location) const;
const std::vector<VariableLocation> &getUniformLocations() const;
const LinkedUniform &getUniformByIndex(GLuint index) const; const std::vector<VariableLocation> &getUniformLocations() const
{
ASSERT(mLinkResolved);
return mState.mUniformLocations;
}
const LinkedUniform &getUniformByIndex(GLuint index) const
{
ASSERT(mLinkResolved);
ASSERT(index < static_cast<size_t>(mState.mUniforms.size()));
return mState.mUniforms[index];
}
const BufferVariable &getBufferVariableByIndex(GLuint index) const; const BufferVariable &getBufferVariableByIndex(GLuint index) const;
...@@ -704,14 +719,14 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -704,14 +719,14 @@ class Program final : angle::NonCopyable, public LabeledObject
const AttributesMask &getActiveAttribLocationsMask() const const AttributesMask &getActiveAttribLocationsMask() const
{ {
resolveLink(); ASSERT(mLinkResolved);
return mState.mActiveAttribLocationsMask; return mState.mActiveAttribLocationsMask;
} }
const std::vector<SamplerBinding> &getSamplerBindings() const; const std::vector<SamplerBinding> &getSamplerBindings() const;
const std::vector<ImageBinding> &getImageBindings() const const std::vector<ImageBinding> &getImageBindings() const
{ {
resolveLink(); ASSERT(mLinkResolved);
return mState.mImageBindings; return mState.mImageBindings;
} }
const sh::WorkGroupSize &getComputeShaderLocalSize() const; const sh::WorkGroupSize &getComputeShaderLocalSize() const;
...@@ -722,7 +737,7 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -722,7 +737,7 @@ class Program final : angle::NonCopyable, public LabeledObject
const ProgramState &getState() const const ProgramState &getState() const
{ {
resolveLink(); ASSERT(mLinkResolved);
return mState; return mState;
} }
...@@ -780,6 +795,15 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -780,6 +795,15 @@ class Program final : angle::NonCopyable, public LabeledObject
Error syncState(const Context *context); Error syncState(const Context *context);
// Try to resolve linking. Inlined to make sure its overhead is as low as possible.
void resolveLink()
{
if (!mLinkResolved)
{
resolveLinkImpl();
}
}
private: private:
struct LinkingState; struct LinkingState;
...@@ -873,16 +897,7 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -873,16 +897,7 @@ class Program final : angle::NonCopyable, public LabeledObject
GLuint getSamplerUniformBinding(const VariableLocation &uniformLocation) const; GLuint getSamplerUniformBinding(const VariableLocation &uniformLocation) const;
bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps); bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps);
// Try to resolve linking.
// Note: this method is frequently called in each draw call. Please make sure its overhead
// is as low as possible.
void resolveLink() const
{
if (!mLinkResolved)
{
return const_cast<Program *>(this)->resolveLinkImpl();
}
}
// Block until linking is finished and resolve it. // Block until linking is finished and resolve it.
void resolveLinkImpl(); void resolveLinkImpl();
......
...@@ -227,7 +227,21 @@ class State : angle::NonCopyable ...@@ -227,7 +227,21 @@ class State : angle::NonCopyable
// Program binding manipulation // Program binding manipulation
void setProgram(const Context *context, Program *newProgram); void setProgram(const Context *context, Program *newProgram);
Program *getProgram() const { return mProgram; }
Program *getProgram() const
{
ASSERT(!mProgram || !mProgram->isLinking());
return mProgram;
}
Program *getLinkedProgram() const
{
if (mProgram)
{
mProgram->resolveLink();
}
return mProgram;
}
// Transform feedback object (not buffer) binding manipulation // Transform feedback object (not buffer) binding manipulation
void setTransformFeedbackBinding(const Context *context, TransformFeedback *transformFeedback); void setTransformFeedbackBinding(const Context *context, TransformFeedback *transformFeedback);
......
...@@ -385,7 +385,7 @@ bool ValidateTextureMaxAnisotropyValue(Context *context, GLfloat paramValue) ...@@ -385,7 +385,7 @@ bool ValidateTextureMaxAnisotropyValue(Context *context, GLfloat paramValue)
bool ValidateFragmentShaderColorBufferTypeMatch(Context *context) bool ValidateFragmentShaderColorBufferTypeMatch(Context *context)
{ {
const Program *program = context->getGLState().getProgram(); const Program *program = context->getGLState().getLinkedProgram();
const Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer(); const Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer();
return ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(), return ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(),
...@@ -397,7 +397,7 @@ bool ValidateFragmentShaderColorBufferTypeMatch(Context *context) ...@@ -397,7 +397,7 @@ bool ValidateFragmentShaderColorBufferTypeMatch(Context *context)
bool ValidateVertexShaderAttributeTypeMatch(Context *context) bool ValidateVertexShaderAttributeTypeMatch(Context *context)
{ {
const auto &glState = context->getGLState(); const auto &glState = context->getGLState();
const Program *program = context->getGLState().getProgram(); const Program *program = context->getGLState().getLinkedProgram();
const VertexArray *vao = context->getGLState().getVertexArray(); const VertexArray *vao = context->getGLState().getVertexArray();
unsigned long stateCurrentValuesTypeBits = glState.getCurrentValuesTypeMask().to_ulong(); unsigned long stateCurrentValuesTypeBits = glState.getCurrentValuesTypeMask().to_ulong();
...@@ -655,7 +655,7 @@ bool ValidateDrawInstancedANGLE(Context *context) ...@@ -655,7 +655,7 @@ bool ValidateDrawInstancedANGLE(Context *context)
// Verify there is at least one active attribute with a divisor of zero // Verify there is at least one active attribute with a divisor of zero
const State &state = context->getGLState(); const State &state = context->getGLState();
Program *program = state.getProgram(); Program *program = state.getLinkedProgram();
const auto &attribs = state.getVertexArray()->getVertexAttributes(); const auto &attribs = state.getVertexArray()->getVertexAttributes();
const auto &bindings = state.getVertexArray()->getVertexBindings(); const auto &bindings = state.getVertexArray()->getVertexBindings();
...@@ -1011,14 +1011,14 @@ bool ValidateWebGLVertexAttribPointer(Context *context, ...@@ -1011,14 +1011,14 @@ bool ValidateWebGLVertexAttribPointer(Context *context,
return true; return true;
} }
Program *GetValidProgram(Context *context, GLuint id) Program *GetValidProgramNoResolve(Context *context, GLuint id)
{ {
// ES3 spec (section 2.11.1) -- "Commands that accept shader or program object names will // ES3 spec (section 2.11.1) -- "Commands that accept shader or program object names will
// generate the error INVALID_VALUE if the provided name is not the name of either a shader // generate the error INVALID_VALUE if the provided name is not the name of either a shader
// or program object and INVALID_OPERATION if the provided name identifies an object // or program object and INVALID_OPERATION if the provided name identifies an object
// that is not the expected type." // that is not the expected type."
Program *validProgram = context->getProgram(id); Program *validProgram = context->getProgramNoResolveLink(id);
if (!validProgram) if (!validProgram)
{ {
...@@ -1035,6 +1035,16 @@ Program *GetValidProgram(Context *context, GLuint id) ...@@ -1035,6 +1035,16 @@ Program *GetValidProgram(Context *context, GLuint id)
return validProgram; return validProgram;
} }
Program *GetValidProgram(Context *context, GLuint id)
{
Program *program = GetValidProgramNoResolve(context, id);
if (program)
{
program->resolveLink();
}
return program;
}
Shader *GetValidShader(Context *context, GLuint id) Shader *GetValidShader(Context *context, GLuint id)
{ {
// See ValidProgram for spec details. // See ValidProgram for spec details.
...@@ -1043,7 +1053,7 @@ Shader *GetValidShader(Context *context, GLuint id) ...@@ -1043,7 +1053,7 @@ Shader *GetValidShader(Context *context, GLuint id)
if (!validShader) if (!validShader)
{ {
if (context->getProgram(id)) if (context->getProgramNoResolveLink(id))
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExpectedShaderName); ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExpectedShaderName);
} }
...@@ -2140,7 +2150,7 @@ bool ValidateUniformMatrixValue(Context *context, GLenum valueType, GLenum unifo ...@@ -2140,7 +2150,7 @@ bool ValidateUniformMatrixValue(Context *context, GLenum valueType, GLenum unifo
bool ValidateUniform(Context *context, GLenum valueType, GLint location, GLsizei count) bool ValidateUniform(Context *context, GLenum valueType, GLint location, GLsizei count)
{ {
const LinkedUniform *uniform = nullptr; const LinkedUniform *uniform = nullptr;
Program *programObject = context->getGLState().getProgram(); Program *programObject = context->getGLState().getLinkedProgram();
return ValidateUniformCommonBase(context, programObject, location, count, &uniform) && return ValidateUniformCommonBase(context, programObject, location, count, &uniform) &&
ValidateUniformValue(context, valueType, uniform->type); ValidateUniformValue(context, valueType, uniform->type);
} }
...@@ -2148,7 +2158,7 @@ bool ValidateUniform(Context *context, GLenum valueType, GLint location, GLsizei ...@@ -2148,7 +2158,7 @@ bool ValidateUniform(Context *context, GLenum valueType, GLint location, GLsizei
bool ValidateUniform1iv(Context *context, GLint location, GLsizei count, const GLint *value) bool ValidateUniform1iv(Context *context, GLint location, GLsizei count, const GLint *value)
{ {
const LinkedUniform *uniform = nullptr; const LinkedUniform *uniform = nullptr;
Program *programObject = context->getGLState().getProgram(); Program *programObject = context->getGLState().getLinkedProgram();
return ValidateUniformCommonBase(context, programObject, location, count, &uniform) && return ValidateUniformCommonBase(context, programObject, location, count, &uniform) &&
ValidateUniform1ivValue(context, uniform->type, count, value); ValidateUniform1ivValue(context, uniform->type, count, value);
} }
...@@ -2166,7 +2176,7 @@ bool ValidateUniformMatrix(Context *context, ...@@ -2166,7 +2176,7 @@ bool ValidateUniformMatrix(Context *context,
} }
const LinkedUniform *uniform = nullptr; const LinkedUniform *uniform = nullptr;
Program *programObject = context->getGLState().getProgram(); Program *programObject = context->getGLState().getLinkedProgram();
return ValidateUniformCommonBase(context, programObject, location, count, &uniform) && return ValidateUniformCommonBase(context, programObject, location, count, &uniform) &&
ValidateUniformMatrixValue(context, valueType, uniform->type); ValidateUniformMatrixValue(context, valueType, uniform->type);
} }
...@@ -2647,7 +2657,7 @@ const char *ValidateDrawStates(Context *context) ...@@ -2647,7 +2657,7 @@ const char *ValidateDrawStates(Context *context)
// If we are running GLES1, there is no current program. // If we are running GLES1, there is no current program.
if (context->getClientVersion() >= Version(2, 0)) if (context->getClientVersion() >= Version(2, 0))
{ {
Program *program = state.getProgram(); Program *program = state.getLinkedProgram();
if (!program) if (!program)
{ {
return kErrorProgramNotBound; return kErrorProgramNotBound;
...@@ -2794,7 +2804,7 @@ bool ValidateDrawMode(Context *context, PrimitiveMode mode) ...@@ -2794,7 +2804,7 @@ bool ValidateDrawMode(Context *context, PrimitiveMode mode)
{ {
const State &state = context->getGLState(); const State &state = context->getGLState();
Program *program = state.getProgram(); Program *program = state.getLinkedProgram();
ASSERT(program); ASSERT(program);
// Do geometry shader specific validations // Do geometry shader specific validations
...@@ -3269,7 +3279,7 @@ static bool ValidateSizedGetUniform(Context *context, ...@@ -3269,7 +3279,7 @@ static bool ValidateSizedGetUniform(Context *context,
return false; return false;
} }
Program *programObject = context->getProgram(program); Program *programObject = context->getProgramResolveLink(program);
ASSERT(programObject); ASSERT(programObject);
// sized queries -- ensure the provided buffer is large enough // sized queries -- ensure the provided buffer is large enough
...@@ -4405,7 +4415,10 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL ...@@ -4405,7 +4415,10 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL
*numParams = 1; *numParams = 1;
} }
Program *programObject = GetValidProgram(context, program); // Special case for GL_COMPLETION_STATUS_KHR: don't resolve the link. Otherwise resolve it now.
Program *programObject = (pname == GL_COMPLETION_STATUS_KHR)
? GetValidProgramNoResolve(context, program)
: GetValidProgram(context, program);
if (!programObject) if (!programObject)
{ {
return false; return false;
...@@ -4415,7 +4428,6 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL ...@@ -4415,7 +4428,6 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL
{ {
case GL_DELETE_STATUS: case GL_DELETE_STATUS:
case GL_LINK_STATUS: case GL_LINK_STATUS:
case GL_COMPLETION_STATUS_KHR:
case GL_VALIDATE_STATUS: case GL_VALIDATE_STATUS:
case GL_INFO_LOG_LENGTH: case GL_INFO_LOG_LENGTH:
case GL_ATTACHED_SHADERS: case GL_ATTACHED_SHADERS:
...@@ -4507,6 +4519,14 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL ...@@ -4507,6 +4519,14 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL
} }
break; break;
case GL_COMPLETION_STATUS_KHR:
if (!context->getExtensions().parallelShaderCompile)
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
break;
default: default:
ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported); ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
return false; return false;
...@@ -5357,7 +5377,6 @@ bool ValidateGetShaderivBase(Context *context, GLuint shader, GLenum pname, GLsi ...@@ -5357,7 +5377,6 @@ bool ValidateGetShaderivBase(Context *context, GLuint shader, GLenum pname, GLsi
case GL_SHADER_TYPE: case GL_SHADER_TYPE:
case GL_DELETE_STATUS: case GL_DELETE_STATUS:
case GL_COMPILE_STATUS: case GL_COMPILE_STATUS:
case GL_COMPLETION_STATUS_KHR:
case GL_INFO_LOG_LENGTH: case GL_INFO_LOG_LENGTH:
case GL_SHADER_SOURCE_LENGTH: case GL_SHADER_SOURCE_LENGTH:
break; break;
...@@ -5370,6 +5389,14 @@ bool ValidateGetShaderivBase(Context *context, GLuint shader, GLenum pname, GLsi ...@@ -5370,6 +5389,14 @@ bool ValidateGetShaderivBase(Context *context, GLuint shader, GLenum pname, GLsi
} }
break; break;
case GL_COMPLETION_STATUS_KHR:
if (!context->getExtensions().parallelShaderCompile)
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
return false;
}
break;
default: default:
ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported); ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
return false; return false;
......
...@@ -2250,7 +2250,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, ...@@ -2250,7 +2250,7 @@ static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier,
return true; return true;
case GL_PROGRAM: case GL_PROGRAM:
if (context->getProgram(name) == nullptr) if (context->getProgramNoResolveLink(name) == nullptr)
{ {
context->handleError(InvalidValue() << "name is not a valid program."); context->handleError(InvalidValue() << "name is not a valid program.");
return false; return false;
...@@ -3843,7 +3843,7 @@ bool ValidateBindFragmentInputLocationCHROMIUM(Context *context, ...@@ -3843,7 +3843,7 @@ bool ValidateBindFragmentInputLocationCHROMIUM(Context *context,
return false; return false;
} }
const auto *programObject = context->getProgram(program); const auto *programObject = context->getProgramNoResolveLink(program);
if (!programObject) if (!programObject)
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound); ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
...@@ -3878,7 +3878,7 @@ bool ValidateProgramPathFragmentInputGenCHROMIUM(Context *context, ...@@ -3878,7 +3878,7 @@ bool ValidateProgramPathFragmentInputGenCHROMIUM(Context *context,
return false; return false;
} }
const auto *programObject = context->getProgram(program); const auto *programObject = context->getProgramResolveLink(program);
if (!programObject || programObject->isFlaggedForDeletion()) if (!programObject || programObject->isFlaggedForDeletion())
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramDoesNotExist); ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramDoesNotExist);
...@@ -4933,7 +4933,7 @@ bool ValidateDeleteProgram(Context *context, GLuint program) ...@@ -4933,7 +4933,7 @@ bool ValidateDeleteProgram(Context *context, GLuint program)
return false; return false;
} }
if (!context->getProgram(program)) if (!context->getProgramResolveLink(program))
{ {
if (context->getShader(program)) if (context->getShader(program))
{ {
...@@ -4959,7 +4959,7 @@ bool ValidateDeleteShader(Context *context, GLuint shader) ...@@ -4959,7 +4959,7 @@ bool ValidateDeleteShader(Context *context, GLuint shader)
if (!context->getShader(shader)) if (!context->getShader(shader))
{ {
if (context->getProgram(shader)) if (context->getProgramResolveLink(shader))
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidShaderName); ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidShaderName);
return false; return false;
...@@ -6322,7 +6322,7 @@ bool ValidateUseProgram(Context *context, GLuint program) ...@@ -6322,7 +6322,7 @@ bool ValidateUseProgram(Context *context, GLuint program)
{ {
if (program != 0) if (program != 0)
{ {
Program *programObject = context->getProgram(program); Program *programObject = context->getProgramResolveLink(program);
if (!programObject) if (!programObject)
{ {
// ES 3.1.0 section 7.3 page 72 // ES 3.1.0 section 7.3 page 72
......
...@@ -2192,7 +2192,7 @@ bool ValidateBeginTransformFeedback(Context *context, PrimitiveMode primitiveMod ...@@ -2192,7 +2192,7 @@ bool ValidateBeginTransformFeedback(Context *context, PrimitiveMode primitiveMod
} }
} }
Program *program = context->getGLState().getProgram(); Program *program = context->getGLState().getLinkedProgram();
if (!program) if (!program)
{ {
......
...@@ -1419,7 +1419,7 @@ bool ValidateDispatchCompute(Context *context, ...@@ -1419,7 +1419,7 @@ bool ValidateDispatchCompute(Context *context,
} }
const State &state = context->getGLState(); const State &state = context->getGLState();
Program *program = state.getProgram(); Program *program = state.getLinkedProgram();
if (program == nullptr || !program->hasLinkedShaderStage(ShaderType::Compute)) if (program == nullptr || !program->hasLinkedShaderStage(ShaderType::Compute))
{ {
...@@ -1462,7 +1462,7 @@ bool ValidateDispatchComputeIndirect(Context *context, GLintptr indirect) ...@@ -1462,7 +1462,7 @@ bool ValidateDispatchComputeIndirect(Context *context, GLintptr indirect)
} }
const State &state = context->getGLState(); const State &state = context->getGLState();
Program *program = state.getProgram(); Program *program = state.getLinkedProgram();
if (program == nullptr || !program->hasLinkedShaderStage(ShaderType::Compute)) if (program == nullptr || !program->hasLinkedShaderStage(ShaderType::Compute))
{ {
......
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