blob: 10b7b16dd9a72256b0a452dfa98b207caf0c74c8 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gl/GrGLInterface.h"
#include "gl/GrGLExtensions.h"
#include "gl/GrGLUtil.h"
#include <stdio.h>
GrGLInterface::GrGLInterface() {
fStandard = kNone_GrGLStandard;
}
#ifdef SK_DEBUG
static int kIsDebug = 1;
#else
static int kIsDebug = 0;
#endif
#define RETURN_FALSE_INTERFACE \
if (kIsDebug) { SkDebugf("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); } \
return false;
bool GrGLInterface::validate() const {
if (kNone_GrGLStandard == fStandard) {
RETURN_FALSE_INTERFACE
}
if (!fExtensions.isInitialized()) {
RETURN_FALSE_INTERFACE
}
// functions that are always required
if (!fFunctions.fActiveTexture ||
!fFunctions.fAttachShader ||
!fFunctions.fBindAttribLocation ||
!fFunctions.fBindBuffer ||
!fFunctions.fBindTexture ||
!fFunctions.fBlendColor || // -> GL >= 1.4 or extension, ES >= 2.0
!fFunctions.fBlendEquation || // -> GL >= 1.4 or extension, ES >= 2.0
!fFunctions.fBlendFunc ||
!fFunctions.fBufferData ||
!fFunctions.fBufferSubData ||
!fFunctions.fClear ||
!fFunctions.fClearColor ||
!fFunctions.fClearStencil ||
!fFunctions.fColorMask ||
!fFunctions.fCompileShader ||
!fFunctions.fCompressedTexImage2D ||
!fFunctions.fCompressedTexSubImage2D ||
!fFunctions.fCopyTexSubImage2D ||
!fFunctions.fCreateProgram ||
!fFunctions.fCreateShader ||
!fFunctions.fCullFace ||
!fFunctions.fDeleteBuffers ||
!fFunctions.fDeleteProgram ||
!fFunctions.fDeleteShader ||
!fFunctions.fDeleteTextures ||
!fFunctions.fDepthMask ||
!fFunctions.fDisable ||
!fFunctions.fDisableVertexAttribArray ||
!fFunctions.fDrawArrays ||
!fFunctions.fDrawElements ||
!fFunctions.fEnable ||
!fFunctions.fEnableVertexAttribArray ||
!fFunctions.fFrontFace ||
!fFunctions.fGenBuffers ||
!fFunctions.fGenTextures ||
!fFunctions.fGetBufferParameteriv ||
!fFunctions.fGenerateMipmap ||
!fFunctions.fGetError ||
!fFunctions.fGetIntegerv ||
!fFunctions.fGetProgramInfoLog ||
!fFunctions.fGetProgramiv ||
!fFunctions.fGetShaderInfoLog ||
!fFunctions.fGetShaderiv ||
!fFunctions.fGetString ||
!fFunctions.fGetUniformLocation ||
!fFunctions.fIsTexture ||
!fFunctions.fLinkProgram ||
!fFunctions.fLineWidth ||
!fFunctions.fPixelStorei ||
!fFunctions.fReadPixels ||
!fFunctions.fScissor ||
!fFunctions.fShaderSource ||
!fFunctions.fStencilFunc ||
!fFunctions.fStencilFuncSeparate ||
!fFunctions.fStencilMask ||
!fFunctions.fStencilMaskSeparate ||
!fFunctions.fStencilOp ||
!fFunctions.fStencilOpSeparate ||
!fFunctions.fTexImage2D ||
!fFunctions.fTexParameteri ||
!fFunctions.fTexParameteriv ||
!fFunctions.fTexSubImage2D ||
!fFunctions.fUniform1f ||
!fFunctions.fUniform1i ||
!fFunctions.fUniform1fv ||
!fFunctions.fUniform1iv ||
!fFunctions.fUniform2f ||
!fFunctions.fUniform2i ||
!fFunctions.fUniform2fv ||
!fFunctions.fUniform2iv ||
!fFunctions.fUniform3f ||
!fFunctions.fUniform3i ||
!fFunctions.fUniform3fv ||
!fFunctions.fUniform3iv ||
!fFunctions.fUniform4f ||
!fFunctions.fUniform4i ||
!fFunctions.fUniform4fv ||
!fFunctions.fUniform4iv ||
!fFunctions.fUniformMatrix2fv ||
!fFunctions.fUniformMatrix3fv ||
!fFunctions.fUniformMatrix4fv ||
!fFunctions.fUseProgram ||
!fFunctions.fVertexAttrib1f ||
!fFunctions.fVertexAttrib2fv ||
!fFunctions.fVertexAttrib3fv ||
!fFunctions.fVertexAttrib4fv ||
!fFunctions.fVertexAttribPointer ||
!fFunctions.fViewport ||
!fFunctions.fBindFramebuffer ||
!fFunctions.fBindRenderbuffer ||
!fFunctions.fCheckFramebufferStatus ||
!fFunctions.fDeleteFramebuffers ||
!fFunctions.fDeleteRenderbuffers ||
!fFunctions.fFinish ||
!fFunctions.fFlush ||
!fFunctions.fFramebufferRenderbuffer ||
!fFunctions.fFramebufferTexture2D ||
!fFunctions.fGetFramebufferAttachmentParameteriv ||
!fFunctions.fGetRenderbufferParameteriv ||
!fFunctions.fGenFramebuffers ||
!fFunctions.fGenRenderbuffers ||
!fFunctions.fRenderbufferStorage) {
RETURN_FALSE_INTERFACE
}
GrGLVersion glVer = GrGLGetVersion(this);
if (GR_GL_INVALID_VER == glVer) {
RETURN_FALSE_INTERFACE
}
// Now check that baseline ES/Desktop fns not covered above are present
// and that we have fn pointers for any advertised fExtensions that we will
// try to use.
// these functions are part of ES2, we assume they are available
// On the desktop we assume they are available if the extension
// is present or GL version is high enough.
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,0) && !fFunctions.fBindFragDataLocation) {
RETURN_FALSE_INTERFACE
}
if (glVer >= GR_GL_VER(3,3) ||
fExtensions.has("GL_ARB_timer_query") ||
fExtensions.has("GL_EXT_timer_query")) {
if (!fFunctions.fGetQueryObjecti64v ||
!fFunctions.fGetQueryObjectui64v) {
RETURN_FALSE_INTERFACE
}
}
if (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_timer_query")) {
if (!fFunctions.fQueryCounter) {
RETURN_FALSE_INTERFACE
}
}
}
// part of desktop GL, but not ES
if (kGL_GrGLStandard == fStandard &&
(!fFunctions.fDrawBuffer ||
!fFunctions.fPolygonMode)) {
RETURN_FALSE_INTERFACE
}
// ES 3.0 (or ES 2.0 extended) has glDrawBuffers but not glDrawBuffer
if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0)) {
if (!fFunctions.fDrawBuffers) {
RETURN_FALSE_INTERFACE
}
}
if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0)) {
if (!fFunctions.fReadBuffer) {
RETURN_FALSE_INTERFACE
}
}
// glGetTexLevelParameteriv was added to ES in 3.1.
if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,1)) {
if (!fFunctions.fGetTexLevelParameteriv) {
RETURN_FALSE_INTERFACE
}
}
// GL_EXT_texture_storage is part of desktop 4.2
// There is a desktop ARB extension and an ES+desktop EXT extension
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(4,2) ||
fExtensions.has("GL_ARB_texture_storage") ||
fExtensions.has("GL_EXT_texture_storage")) {
if (!fFunctions.fTexStorage2D) {
RETURN_FALSE_INTERFACE
}
}
} else if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_EXT_texture_storage")) {
if (!fFunctions.fTexStorage2D) {
RETURN_FALSE_INTERFACE
}
}
// glTextureBarrier is part of desktop 4.5. There are also ARB and NV extensions.
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(4,5) ||
fExtensions.has("GL_ARB_texture_barrier") ||
fExtensions.has("GL_NV_texture_barrier")) {
if (!fFunctions.fTextureBarrier) {
RETURN_FALSE_INTERFACE
}
}
} else if (fExtensions.has("GL_NV_texture_barrier")) {
if (!fFunctions.fTextureBarrier) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_KHR_blend_equation_advanced") ||
fExtensions.has("GL_NV_blend_equation_advanced")) {
if (!fFunctions.fBlendBarrier) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_EXT_discard_framebuffer")) {
if (!fFunctions.fDiscardFramebuffer) {
RETURN_FALSE_INTERFACE
}
}
// Required since OpenGL 1.5 and ES 3.0 or with GL_EXT_occlusion_query_boolean
if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0) ||
fExtensions.has("GL_EXT_occlusion_query_boolean")) {
#if 0 // Not yet added to chrome's bindings.
if (!fFunctions.fGenQueries ||
!fFunctions.fDeleteQueries ||
!fFunctions.fBeginQuery ||
!fFunctions.fEndQuery ||
!fFunctions.fGetQueryiv ||
!fFunctions.fGetQueryObjectuiv) {
RETURN_FALSE_INTERFACE
}
#endif
}
// glGetQueryObjectiv doesn't exist in ES.
if (kGL_GrGLStandard == fStandard && !fFunctions.fGetQueryObjectiv) {
RETURN_FALSE_INTERFACE
}
// FBO MSAA
if (kGL_GrGLStandard == fStandard) {
// GL 3.0 and the ARB extension have multisample + blit
if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_ARB_framebuffer_object")) {
if (!fFunctions.fRenderbufferStorageMultisample ||
!fFunctions.fBlitFramebuffer) {
RETURN_FALSE_INTERFACE
}
} else {
if (fExtensions.has("GL_EXT_framebuffer_blit") &&
!fFunctions.fBlitFramebuffer) {
RETURN_FALSE_INTERFACE
}
if (fExtensions.has("GL_EXT_framebuffer_multisample") &&
!fFunctions.fRenderbufferStorageMultisample) {
RETURN_FALSE_INTERFACE
}
}
} else {
if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_CHROMIUM_framebuffer_multisample")) {
if (!fFunctions.fRenderbufferStorageMultisample ||
!fFunctions.fBlitFramebuffer) {
RETURN_FALSE_INTERFACE
}
} else {
if (fExtensions.has("GL_ANGLE_framebuffer_multisample") &&
!fFunctions.fRenderbufferStorageMultisample) {
RETURN_FALSE_INTERFACE
}
if (fExtensions.has("GL_ANGLE_framebuffer_blit") &&
!fFunctions.fBlitFramebuffer) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_APPLE_framebuffer_multisample")) {
if (!fFunctions.fRenderbufferStorageMultisampleES2APPLE ||
!fFunctions.fResolveMultisampleFramebuffer) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_IMG_multisampled_render_to_texture") ||
fExtensions.has("GL_EXT_multisampled_render_to_texture")) {
if (!fFunctions.fRenderbufferStorageMultisampleES2EXT ||
!fFunctions.fFramebufferTexture2DMultisample) {
RETURN_FALSE_INTERFACE
}
}
}
// On ES buffer mapping is an extension. On Desktop
// buffer mapping was part of original VBO extension
// which we require.
if (kGL_GrGLStandard == fStandard || fExtensions.has("GL_OES_mapbuffer")) {
if (!fFunctions.fMapBuffer ||
!fFunctions.fUnmapBuffer) {
RETURN_FALSE_INTERFACE
}
}
// Dual source blending
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_blend_func_extended")) {
if (!fFunctions.fBindFragDataLocationIndexed) {
RETURN_FALSE_INTERFACE
}
}
} else {
if (glVer >= GR_GL_VER(3,0) && fExtensions.has("GL_EXT_blend_func_extended")) {
if (!fFunctions.fBindFragDataLocation ||
!fFunctions.fBindFragDataLocationIndexed) {
RETURN_FALSE_INTERFACE
}
}
}
// glGetStringi was added in version 3.0 of both desktop and ES.
if (glVer >= GR_GL_VER(3, 0)) {
if (!fFunctions.fGetStringi) {
RETURN_FALSE_INTERFACE
}
}
// glVertexAttribIPointer was added in version 3.0 of both desktop and ES.
if (glVer >= GR_GL_VER(3, 0)) {
if (!fFunctions.fVertexAttribIPointer) {
RETURN_FALSE_INTERFACE
}
}
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,1)) {
if (!fFunctions.fTexBuffer) {
RETURN_FALSE_INTERFACE;
}
}
if (glVer >= GR_GL_VER(4,3)) {
if (!fFunctions.fTexBufferRange) {
RETURN_FALSE_INTERFACE;
}
}
} else {
if (glVer >= GR_GL_VER(3,2) || fExtensions.has("GL_OES_texture_buffer") ||
fExtensions.has("GL_EXT_texture_buffer")) {
if (!fFunctions.fTexBuffer ||
!fFunctions.fTexBufferRange) {
RETURN_FALSE_INTERFACE;
}
}
}
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3, 0) || fExtensions.has("GL_ARB_vertex_array_object")) {
if (!fFunctions.fBindVertexArray ||
!fFunctions.fDeleteVertexArrays ||
!fFunctions.fGenVertexArrays) {
RETURN_FALSE_INTERFACE
}
}
} else {
if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_OES_vertex_array_object")) {
if (!fFunctions.fBindVertexArray ||
!fFunctions.fDeleteVertexArrays ||
!fFunctions.fGenVertexArrays) {
RETURN_FALSE_INTERFACE
}
}
}
if (fExtensions.has("GL_EXT_debug_marker")) {
if (!fFunctions.fInsertEventMarker ||
!fFunctions.fPushGroupMarker ||
!fFunctions.fPopGroupMarker) {
RETURN_FALSE_INTERFACE
}
}
if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,3)) ||
fExtensions.has("GL_ARB_invalidate_subdata")) {
if (!fFunctions.fInvalidateBufferData ||
!fFunctions.fInvalidateBufferSubData ||
!fFunctions.fInvalidateFramebuffer ||
!fFunctions.fInvalidateSubFramebuffer ||
!fFunctions.fInvalidateTexImage ||
!fFunctions.fInvalidateTexSubImage) {
RETURN_FALSE_INTERFACE;
}
} else if (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0)) {
// ES 3.0 adds the framebuffer functions but not the others.
if (!fFunctions.fInvalidateFramebuffer ||
!fFunctions.fInvalidateSubFramebuffer) {
RETURN_FALSE_INTERFACE;
}
}
if (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_CHROMIUM_map_sub")) {
if (!fFunctions.fMapBufferSubData ||
!fFunctions.fMapTexSubImage2D ||
!fFunctions.fUnmapBufferSubData ||
!fFunctions.fUnmapTexSubImage2D) {
RETURN_FALSE_INTERFACE;
}
}
// These functions are added to the 3.0 version of both GLES and GL.
if (glVer >= GR_GL_VER(3,0) ||
(kGLES_GrGLStandard == fStandard && fExtensions.has("GL_EXT_map_buffer_range")) ||
(kGL_GrGLStandard == fStandard && fExtensions.has("GL_ARB_map_buffer_range"))) {
if (!fFunctions.fMapBufferRange ||
!fFunctions.fFlushMappedBufferRange) {
RETURN_FALSE_INTERFACE;
}
}
if ((kGL_GrGLStandard == fStandard &&
(glVer >= GR_GL_VER(3,2) || fExtensions.has("GL_ARB_texture_multisample"))) ||
(kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,1))) {
if (!fFunctions.fGetMultisamplefv) {
RETURN_FALSE_INTERFACE
}
}
if ((kGL_GrGLStandard == fStandard &&
(glVer >= GR_GL_VER(4,3) || fExtensions.has("GL_ARB_program_interface_query"))) ||
(kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,1))) {
if (!fFunctions.fGetProgramResourceLocation) {
RETURN_FALSE_INTERFACE
}
}
if (kGLES_GrGLStandard == fStandard || glVer >= GR_GL_VER(4,1) ||
fExtensions.has("GL_ARB_ES2_compatibility")) {
if (!fFunctions.fGetShaderPrecisionFormat) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_NV_path_rendering") || fExtensions.has("GL_CHROMIUM_path_rendering")) {
if (!fFunctions.fMatrixLoadf ||
!fFunctions.fMatrixLoadIdentity ||
!fFunctions.fPathCommands ||
!fFunctions.fPathParameteri ||
!fFunctions.fPathParameterf ||
!fFunctions.fGenPaths ||
!fFunctions.fDeletePaths ||
!fFunctions.fIsPath ||
!fFunctions.fPathStencilFunc ||
!fFunctions.fStencilFillPath ||
!fFunctions.fStencilStrokePath ||
!fFunctions.fStencilFillPathInstanced ||
!fFunctions.fStencilStrokePathInstanced ||
!fFunctions.fCoverFillPath ||
!fFunctions.fCoverStrokePath ||
!fFunctions.fCoverFillPathInstanced ||
!fFunctions.fCoverStrokePathInstanced
#if 0
// List of functions that Skia uses, but which have been added since the initial release
// of NV_path_rendering driver. We do not want to fail interface validation due to
// missing features, we will just not use the extension.
// Update this list -> update GrGLCaps::hasPathRenderingSupport too.
|| !fFunctions.fStencilThenCoverFillPath ||
!fFunctions.fStencilThenCoverStrokePath ||
!fFunctions.fStencilThenCoverFillPathInstanced ||
!fFunctions.fStencilThenCoverStrokePathInstanced ||
!fFunctions.fProgramPathFragmentInputGen
#endif
) {
RETURN_FALSE_INTERFACE
}
if (fExtensions.has("GL_CHROMIUM_path_rendering")) {
if (!fFunctions.fBindFragmentInputLocation) {
RETURN_FALSE_INTERFACE
}
}
}
if (fExtensions.has("GL_EXT_raster_multisample")) {
if (!fFunctions.fRasterSamples) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_NV_framebuffer_mixed_samples") ||
fExtensions.has("GL_CHROMIUM_framebuffer_mixed_samples")) {
if (!fFunctions.fCoverageModulation) {
RETURN_FALSE_INTERFACE
}
}
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,1) ||
fExtensions.has("GL_EXT_draw_instanced") || fExtensions.has("GL_ARB_draw_instanced")) {
if (!fFunctions.fDrawArraysInstanced ||
!fFunctions.fDrawElementsInstanced) {
RETURN_FALSE_INTERFACE
}
}
} else if (kGLES_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_EXT_draw_instanced")) {
if (!fFunctions.fDrawArraysInstanced ||
!fFunctions.fDrawElementsInstanced) {
RETURN_FALSE_INTERFACE
}
}
}
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,2) || fExtensions.has("GL_ARB_instanced_arrays")) {
if (!fFunctions.fVertexAttribDivisor) {
RETURN_FALSE_INTERFACE
}
}
} else if (kGLES_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_EXT_instanced_arrays")) {
if (!fFunctions.fVertexAttribDivisor) {
RETURN_FALSE_INTERFACE
}
}
}
if ((kGL_GrGLStandard == fStandard &&
(glVer >= GR_GL_VER(4,0) || fExtensions.has("GL_ARB_draw_indirect"))) ||
(kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,1))) {
if (!fFunctions.fDrawArraysIndirect ||
!fFunctions.fDrawElementsIndirect) {
RETURN_FALSE_INTERFACE
}
}
if ((kGL_GrGLStandard == fStandard &&
(glVer >= GR_GL_VER(4,3) || fExtensions.has("GL_ARB_multi_draw_indirect"))) ||
(kGLES_GrGLStandard == fStandard && fExtensions.has("GL_EXT_multi_draw_indirect"))) {
if (!fFunctions.fMultiDrawArraysIndirect ||
!fFunctions.fMultiDrawElementsIndirect) {
RETURN_FALSE_INTERFACE
}
}
if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,3)) ||
fExtensions.has("GL_KHR_debug")) {
if (!fFunctions.fDebugMessageControl ||
!fFunctions.fDebugMessageInsert ||
!fFunctions.fDebugMessageCallback ||
!fFunctions.fGetDebugMessageLog ||
!fFunctions.fPushDebugGroup ||
!fFunctions.fPopDebugGroup ||
!fFunctions.fObjectLabel) {
RETURN_FALSE_INTERFACE
}
}
if (fExtensions.has("GL_EXT_window_rectangles")) {
if (!fFunctions.fWindowRectangles) {
RETURN_FALSE_INTERFACE
}
}
if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,0)) ||
fExtensions.has("GL_ARB_sample_shading")) {
if (!fFunctions.fMinSampleShading) {
RETURN_FALSE_INTERFACE
}
} else if (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_OES_sample_shading")) {
if (!fFunctions.fMinSampleShading) {
RETURN_FALSE_INTERFACE
}
}
if (kGL_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3, 2) || fExtensions.has("GL_ARB_sync")) {
if (!fFunctions.fFenceSync ||
!fFunctions.fIsSync ||
!fFunctions.fClientWaitSync ||
!fFunctions.fWaitSync ||
!fFunctions.fDeleteSync) {
RETURN_FALSE_INTERFACE
}
}
} else if (kGLES_GrGLStandard == fStandard) {
if (glVer >= GR_GL_VER(3, 0) || fExtensions.has("GL_APPLE_sync")) {
if (!fFunctions.fFenceSync ||
!fFunctions.fIsSync ||
!fFunctions.fClientWaitSync ||
!fFunctions.fWaitSync ||
!fFunctions.fDeleteSync) {
RETURN_FALSE_INTERFACE
}
}
}
if (fExtensions.has("EGL_KHR_image") || fExtensions.has("EGL_KHR_image_base")) {
if (!fFunctions.fEGLCreateImage ||
!fFunctions.fEGLDestroyImage) {
RETURN_FALSE_INTERFACE
}
}
// glDrawRangeElements was added to ES in 3.0.
if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0)) {
if (!fFunctions.fDrawRangeElements) {
RETURN_FALSE_INTERFACE;
}
}
// getInternalformativ was added in GL 4.2, ES 3.0, and with extension ARB_internalformat_query
if ((kGL_GrGLStandard == fStandard &&
(glVer >= GR_GL_VER(4,2) || fExtensions.has("GL_ARB_internalformat_query"))) ||
(kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0))) {
if (!fFunctions.fGetInternalformativ) {
RETURN_FALSE_INTERFACE;
}
}
if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,1)) ||
(kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0))) {
if (!fFunctions.fGetProgramBinary ||
!fFunctions.fProgramBinary ||
!fFunctions.fProgramParameteri) {
RETURN_FALSE_INTERFACE;
}
}
return true;
}