1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 9#include "gl/GrGLInterface.h" 10#include "gl/GrGLExtensions.h" 11#include "gl/GrGLUtil.h" 12 13#include <stdio.h> 14 15#if GR_GL_PER_GL_FUNC_CALLBACK 16namespace { 17void GrGLDefaultInterfaceCallback(const GrGLInterface*) {} 18} 19#endif 20 21const GrGLInterface* GrGLInterfaceAddTestDebugMarker(const GrGLInterface* interface, 22 GrGLInsertEventMarkerProc insertEventMarkerFn, 23 GrGLPushGroupMarkerProc pushGroupMarkerFn, 24 GrGLPopGroupMarkerProc popGroupMarkerFn) { 25 GrGLInterface* newInterface = GrGLInterface::NewClone(interface); 26 27 if (!newInterface->fExtensions.has("GL_EXT_debug_marker")) { 28 newInterface->fExtensions.add("GL_EXT_debug_marker"); 29 } 30 31 newInterface->fFunctions.fInsertEventMarker = insertEventMarkerFn; 32 newInterface->fFunctions.fPushGroupMarker = pushGroupMarkerFn; 33 newInterface->fFunctions.fPopGroupMarker = popGroupMarkerFn; 34 35 return newInterface; 36} 37 38const GrGLInterface* GrGLInterfaceRemoveNVPR(const GrGLInterface* interface) { 39 GrGLInterface* newInterface = GrGLInterface::NewClone(interface); 40 41 newInterface->fExtensions.remove("GL_NV_path_rendering"); 42 43 newInterface->fFunctions.fPathCommands = NULL; 44 newInterface->fFunctions.fPathCoords = NULL; 45 newInterface->fFunctions.fPathSubCommands = NULL; 46 newInterface->fFunctions.fPathSubCoords = NULL; 47 newInterface->fFunctions.fPathString = NULL; 48 newInterface->fFunctions.fPathGlyphs = NULL; 49 newInterface->fFunctions.fPathGlyphRange = NULL; 50 newInterface->fFunctions.fWeightPaths = NULL; 51 newInterface->fFunctions.fCopyPath = NULL; 52 newInterface->fFunctions.fInterpolatePaths = NULL; 53 newInterface->fFunctions.fTransformPath = NULL; 54 newInterface->fFunctions.fPathParameteriv = NULL; 55 newInterface->fFunctions.fPathParameteri = NULL; 56 newInterface->fFunctions.fPathParameterfv = NULL; 57 newInterface->fFunctions.fPathParameterf = NULL; 58 newInterface->fFunctions.fPathDashArray = NULL; 59 newInterface->fFunctions.fGenPaths = NULL; 60 newInterface->fFunctions.fDeletePaths = NULL; 61 newInterface->fFunctions.fIsPath = NULL; 62 newInterface->fFunctions.fPathStencilFunc = NULL; 63 newInterface->fFunctions.fPathStencilDepthOffset = NULL; 64 newInterface->fFunctions.fStencilFillPath = NULL; 65 newInterface->fFunctions.fStencilStrokePath = NULL; 66 newInterface->fFunctions.fStencilFillPathInstanced = NULL; 67 newInterface->fFunctions.fStencilStrokePathInstanced = NULL; 68 newInterface->fFunctions.fPathCoverDepthFunc = NULL; 69 newInterface->fFunctions.fPathColorGen = NULL; 70 newInterface->fFunctions.fPathTexGen = NULL; 71 newInterface->fFunctions.fPathFogGen = NULL; 72 newInterface->fFunctions.fCoverFillPath = NULL; 73 newInterface->fFunctions.fCoverStrokePath = NULL; 74 newInterface->fFunctions.fCoverFillPathInstanced = NULL; 75 newInterface->fFunctions.fCoverStrokePathInstanced = NULL; 76 newInterface->fFunctions.fGetPathParameteriv = NULL; 77 newInterface->fFunctions.fGetPathParameterfv = NULL; 78 newInterface->fFunctions.fGetPathCommands = NULL; 79 newInterface->fFunctions.fGetPathCoords = NULL; 80 newInterface->fFunctions.fGetPathDashArray = NULL; 81 newInterface->fFunctions.fGetPathMetrics = NULL; 82 newInterface->fFunctions.fGetPathMetricRange = NULL; 83 newInterface->fFunctions.fGetPathSpacing = NULL; 84 newInterface->fFunctions.fGetPathColorGeniv = NULL; 85 newInterface->fFunctions.fGetPathColorGenfv = NULL; 86 newInterface->fFunctions.fGetPathTexGeniv = NULL; 87 newInterface->fFunctions.fGetPathTexGenfv = NULL; 88 newInterface->fFunctions.fIsPointInFillPath = NULL; 89 newInterface->fFunctions.fIsPointInStrokePath = NULL; 90 newInterface->fFunctions.fGetPathLength = NULL; 91 newInterface->fFunctions.fPointAlongPath = NULL; 92 93 return newInterface; 94} 95 96GrGLInterface::GrGLInterface() { 97 fStandard = kNone_GrGLStandard; 98 99#if GR_GL_PER_GL_FUNC_CALLBACK 100 fCallback = GrGLDefaultInterfaceCallback; 101 fCallbackData = 0; 102#endif 103} 104 105GrGLInterface* GrGLInterface::NewClone(const GrGLInterface* interface) { 106 SkASSERT(NULL != interface); 107 108 GrGLInterface* clone = SkNEW(GrGLInterface); 109 clone->fStandard = interface->fStandard; 110 clone->fExtensions = interface->fExtensions; 111 clone->fFunctions = interface->fFunctions; 112#if GR_GL_PER_GL_FUNC_CALLBACK 113 clone->fCallback = interface->fCallback; 114 clone->fCallbackData = interface->fCallbackData; 115#endif 116 return clone; 117} 118 119#ifdef SK_DEBUG 120 static int kIsDebug = 1; 121#else 122 static int kIsDebug = 0; 123#endif 124 125#define RETURN_FALSE_INTERFACE \ 126 if (kIsDebug) { SkDebugf("%s:%d GrGLInterface::validate() failed.\n", __FILE__, __LINE__); } \ 127 return false; 128 129bool GrGLInterface::validate() const { 130 131 if (kNone_GrGLStandard == fStandard) { 132 RETURN_FALSE_INTERFACE 133 } 134 135 if (!fExtensions.isInitialized()) { 136 RETURN_FALSE_INTERFACE 137 } 138 139 // functions that are always required 140 if (NULL == fFunctions.fActiveTexture || 141 NULL == fFunctions.fAttachShader || 142 NULL == fFunctions.fBindAttribLocation || 143 NULL == fFunctions.fBindBuffer || 144 NULL == fFunctions.fBindTexture || 145 NULL == fFunctions.fBlendFunc || 146 NULL == fFunctions.fBlendColor || // -> GL >= 1.4, ES >= 2.0 or extension 147 NULL == fFunctions.fBufferData || 148 NULL == fFunctions.fBufferSubData || 149 NULL == fFunctions.fClear || 150 NULL == fFunctions.fClearColor || 151 NULL == fFunctions.fClearStencil || 152 NULL == fFunctions.fColorMask || 153 NULL == fFunctions.fCompileShader || 154 NULL == fFunctions.fCopyTexSubImage2D || 155 NULL == fFunctions.fCreateProgram || 156 NULL == fFunctions.fCreateShader || 157 NULL == fFunctions.fCullFace || 158 NULL == fFunctions.fDeleteBuffers || 159 NULL == fFunctions.fDeleteProgram || 160 NULL == fFunctions.fDeleteShader || 161 NULL == fFunctions.fDeleteTextures || 162 NULL == fFunctions.fDepthMask || 163 NULL == fFunctions.fDisable || 164 NULL == fFunctions.fDisableVertexAttribArray || 165 NULL == fFunctions.fDrawArrays || 166 NULL == fFunctions.fDrawElements || 167 NULL == fFunctions.fEnable || 168 NULL == fFunctions.fEnableVertexAttribArray || 169 NULL == fFunctions.fFrontFace || 170 NULL == fFunctions.fGenBuffers || 171 NULL == fFunctions.fGenTextures || 172 NULL == fFunctions.fGetBufferParameteriv || 173 NULL == fFunctions.fGenerateMipmap || 174 NULL == fFunctions.fGetError || 175 NULL == fFunctions.fGetIntegerv || 176 NULL == fFunctions.fGetProgramInfoLog || 177 NULL == fFunctions.fGetProgramiv || 178 NULL == fFunctions.fGetShaderInfoLog || 179 NULL == fFunctions.fGetShaderiv || 180 NULL == fFunctions.fGetString || 181 NULL == fFunctions.fGetUniformLocation || 182 NULL == fFunctions.fLinkProgram || 183 NULL == fFunctions.fLineWidth || 184 NULL == fFunctions.fPixelStorei || 185 NULL == fFunctions.fReadPixels || 186 NULL == fFunctions.fScissor || 187 NULL == fFunctions.fShaderSource || 188 NULL == fFunctions.fStencilFunc || 189 NULL == fFunctions.fStencilMask || 190 NULL == fFunctions.fStencilOp || 191 NULL == fFunctions.fTexImage2D || 192 NULL == fFunctions.fTexParameteri || 193 NULL == fFunctions.fTexParameteriv || 194 NULL == fFunctions.fTexSubImage2D || 195 NULL == fFunctions.fUniform1f || 196 NULL == fFunctions.fUniform1i || 197 NULL == fFunctions.fUniform1fv || 198 NULL == fFunctions.fUniform1iv || 199 NULL == fFunctions.fUniform2f || 200 NULL == fFunctions.fUniform2i || 201 NULL == fFunctions.fUniform2fv || 202 NULL == fFunctions.fUniform2iv || 203 NULL == fFunctions.fUniform3f || 204 NULL == fFunctions.fUniform3i || 205 NULL == fFunctions.fUniform3fv || 206 NULL == fFunctions.fUniform3iv || 207 NULL == fFunctions.fUniform4f || 208 NULL == fFunctions.fUniform4i || 209 NULL == fFunctions.fUniform4fv || 210 NULL == fFunctions.fUniform4iv || 211 NULL == fFunctions.fUniformMatrix2fv || 212 NULL == fFunctions.fUniformMatrix3fv || 213 NULL == fFunctions.fUniformMatrix4fv || 214 NULL == fFunctions.fUseProgram || 215 NULL == fFunctions.fVertexAttrib4fv || 216 NULL == fFunctions.fVertexAttribPointer || 217 NULL == fFunctions.fViewport || 218 NULL == fFunctions.fBindFramebuffer || 219 NULL == fFunctions.fBindRenderbuffer || 220 NULL == fFunctions.fCheckFramebufferStatus || 221 NULL == fFunctions.fDeleteFramebuffers || 222 NULL == fFunctions.fDeleteRenderbuffers || 223 NULL == fFunctions.fFinish || 224 NULL == fFunctions.fFlush || 225 NULL == fFunctions.fFramebufferRenderbuffer || 226 NULL == fFunctions.fFramebufferTexture2D || 227 NULL == fFunctions.fGetFramebufferAttachmentParameteriv || 228 NULL == fFunctions.fGetRenderbufferParameteriv || 229 NULL == fFunctions.fGenFramebuffers || 230 NULL == fFunctions.fGenRenderbuffers || 231 NULL == fFunctions.fRenderbufferStorage) { 232 RETURN_FALSE_INTERFACE 233 } 234 235 GrGLVersion glVer = GrGLGetVersion(this); 236 if (GR_GL_INVALID_VER == glVer) { 237 RETURN_FALSE_INTERFACE 238 } 239 240 // Now check that baseline ES/Desktop fns not covered above are present 241 // and that we have fn pointers for any advertised fExtensions that we will 242 // try to use. 243 244 // these functions are part of ES2, we assume they are available 245 // On the desktop we assume they are available if the extension 246 // is present or GL version is high enough. 247 if (kGLES_GrGLStandard == fStandard) { 248 if (NULL == fFunctions.fStencilFuncSeparate || 249 NULL == fFunctions.fStencilMaskSeparate || 250 NULL == fFunctions.fStencilOpSeparate) { 251 RETURN_FALSE_INTERFACE 252 } 253 } else if (kGL_GrGLStandard == fStandard) { 254 255 if (glVer >= GR_GL_VER(2,0)) { 256 if (NULL == fFunctions.fStencilFuncSeparate || 257 NULL == fFunctions.fStencilMaskSeparate || 258 NULL == fFunctions.fStencilOpSeparate) { 259 RETURN_FALSE_INTERFACE 260 } 261 } 262 if (glVer >= GR_GL_VER(3,0) && NULL == fFunctions.fBindFragDataLocation) { 263 RETURN_FALSE_INTERFACE 264 } 265 if (glVer >= GR_GL_VER(2,0) || fExtensions.has("GL_ARB_draw_buffers")) { 266 if (NULL == fFunctions.fDrawBuffers) { 267 RETURN_FALSE_INTERFACE 268 } 269 } 270 271 if (glVer >= GR_GL_VER(1,5) || fExtensions.has("GL_ARB_occlusion_query")) { 272 if (NULL == fFunctions.fGenQueries || 273 NULL == fFunctions.fDeleteQueries || 274 NULL == fFunctions.fBeginQuery || 275 NULL == fFunctions.fEndQuery || 276 NULL == fFunctions.fGetQueryiv || 277 NULL == fFunctions.fGetQueryObjectiv || 278 NULL == fFunctions.fGetQueryObjectuiv) { 279 RETURN_FALSE_INTERFACE 280 } 281 } 282 if (glVer >= GR_GL_VER(3,3) || 283 fExtensions.has("GL_ARB_timer_query") || 284 fExtensions.has("GL_EXT_timer_query")) { 285 if (NULL == fFunctions.fGetQueryObjecti64v || 286 NULL == fFunctions.fGetQueryObjectui64v) { 287 RETURN_FALSE_INTERFACE 288 } 289 } 290 if (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_timer_query")) { 291 if (NULL == fFunctions.fQueryCounter) { 292 RETURN_FALSE_INTERFACE 293 } 294 } 295 if (fExtensions.has("GL_EXT_direct_state_access")) { 296 if (NULL == fFunctions.fMatrixLoadf || 297 NULL == fFunctions.fMatrixLoadIdentity) { 298 RETURN_FALSE_INTERFACE 299 } 300 } 301 if (fExtensions.has("GL_NV_path_rendering")) { 302 if (NULL == fFunctions.fPathCommands || 303 NULL == fFunctions.fPathCoords || 304 NULL == fFunctions.fPathSubCommands || 305 NULL == fFunctions.fPathSubCoords || 306 NULL == fFunctions.fPathString || 307 NULL == fFunctions.fPathGlyphs || 308 NULL == fFunctions.fPathGlyphRange || 309 NULL == fFunctions.fWeightPaths || 310 NULL == fFunctions.fCopyPath || 311 NULL == fFunctions.fInterpolatePaths || 312 NULL == fFunctions.fTransformPath || 313 NULL == fFunctions.fPathParameteriv || 314 NULL == fFunctions.fPathParameteri || 315 NULL == fFunctions.fPathParameterfv || 316 NULL == fFunctions.fPathParameterf || 317 NULL == fFunctions.fPathDashArray || 318 NULL == fFunctions.fGenPaths || 319 NULL == fFunctions.fDeletePaths || 320 NULL == fFunctions.fIsPath || 321 NULL == fFunctions.fPathStencilFunc || 322 NULL == fFunctions.fPathStencilDepthOffset || 323 NULL == fFunctions.fStencilFillPath || 324 NULL == fFunctions.fStencilStrokePath || 325 NULL == fFunctions.fStencilFillPathInstanced || 326 NULL == fFunctions.fStencilStrokePathInstanced || 327 NULL == fFunctions.fPathCoverDepthFunc || 328 NULL == fFunctions.fPathColorGen || 329 NULL == fFunctions.fPathTexGen || 330 NULL == fFunctions.fPathFogGen || 331 NULL == fFunctions.fCoverFillPath || 332 NULL == fFunctions.fCoverStrokePath || 333 NULL == fFunctions.fCoverFillPathInstanced || 334 NULL == fFunctions.fCoverStrokePathInstanced || 335 NULL == fFunctions.fGetPathParameteriv || 336 NULL == fFunctions.fGetPathParameterfv || 337 NULL == fFunctions.fGetPathCommands || 338 NULL == fFunctions.fGetPathCoords || 339 NULL == fFunctions.fGetPathDashArray || 340 NULL == fFunctions.fGetPathMetrics || 341 NULL == fFunctions.fGetPathMetricRange || 342 NULL == fFunctions.fGetPathSpacing || 343 NULL == fFunctions.fGetPathColorGeniv || 344 NULL == fFunctions.fGetPathColorGenfv || 345 NULL == fFunctions.fGetPathTexGeniv || 346 NULL == fFunctions.fGetPathTexGenfv || 347 NULL == fFunctions.fIsPointInFillPath || 348 NULL == fFunctions.fIsPointInStrokePath || 349 NULL == fFunctions.fGetPathLength || 350 NULL == fFunctions.fPointAlongPath) { 351 RETURN_FALSE_INTERFACE 352 } 353 } 354 } 355 356 // optional function on desktop before 1.3 357 if (kGL_GrGLStandard != fStandard || 358 (glVer >= GR_GL_VER(1,3)) || 359 fExtensions.has("GL_ARB_texture_compression")) { 360 if (NULL == fFunctions.fCompressedTexImage2D 361#if 0 362 || NULL == fFunctions.fCompressedTexSubImage2D 363#endif 364 ) { 365 RETURN_FALSE_INTERFACE 366 } 367 } 368 369 // part of desktop GL, but not ES 370 if (kGL_GrGLStandard == fStandard && 371 (NULL == fFunctions.fGetTexLevelParameteriv || 372 NULL == fFunctions.fDrawBuffer || 373 NULL == fFunctions.fReadBuffer)) { 374 RETURN_FALSE_INTERFACE 375 } 376 377 // GL_EXT_texture_storage is part of desktop 4.2 378 // There is a desktop ARB extension and an ES+desktop EXT extension 379 if (kGL_GrGLStandard == fStandard) { 380 if (glVer >= GR_GL_VER(4,2) || 381 fExtensions.has("GL_ARB_texture_storage") || 382 fExtensions.has("GL_EXT_texture_storage")) { 383 if (NULL == fFunctions.fTexStorage2D) { 384 RETURN_FALSE_INTERFACE 385 } 386 } 387 } else if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_EXT_texture_storage")) { 388 if (NULL == fFunctions.fTexStorage2D) { 389 RETURN_FALSE_INTERFACE 390 } 391 } 392 393 if (fExtensions.has("GL_EXT_discard_framebuffer")) { 394// FIXME: Remove this once Chromium is updated to provide this function 395#if 0 396 if (NULL == fFunctions.fDiscardFramebuffer) { 397 RETURN_FALSE_INTERFACE 398 } 399#endif 400 } 401 402 // FBO MSAA 403 if (kGL_GrGLStandard == fStandard) { 404 // GL 3.0 and the ARB extension have multisample + blit 405 if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_ARB_framebuffer_object")) { 406 if (NULL == fFunctions.fRenderbufferStorageMultisample || 407 NULL == fFunctions.fBlitFramebuffer) { 408 RETURN_FALSE_INTERFACE 409 } 410 } else { 411 if (fExtensions.has("GL_EXT_framebuffer_blit") && 412 NULL == fFunctions.fBlitFramebuffer) { 413 RETURN_FALSE_INTERFACE 414 } 415 if (fExtensions.has("GL_EXT_framebuffer_multisample") && 416 NULL == fFunctions.fRenderbufferStorageMultisample) { 417 RETURN_FALSE_INTERFACE 418 } 419 } 420 } else { 421 if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_CHROMIUM_framebuffer_multisample")) { 422 if (NULL == fFunctions.fRenderbufferStorageMultisample || 423 NULL == fFunctions.fBlitFramebuffer) { 424 RETURN_FALSE_INTERFACE 425 } 426 } 427 if (fExtensions.has("GL_APPLE_framebuffer_multisample")) { 428 if (NULL == fFunctions.fRenderbufferStorageMultisampleES2APPLE || 429 NULL == fFunctions.fResolveMultisampleFramebuffer) { 430 RETURN_FALSE_INTERFACE 431 } 432 } 433 if (fExtensions.has("GL_IMG_multisampled_render_to_texture") || 434 fExtensions.has("GL_EXT_multisampled_render_to_texture")) { 435 if (NULL == fFunctions.fRenderbufferStorageMultisampleES2EXT || 436 NULL == fFunctions.fFramebufferTexture2DMultisample) { 437 RETURN_FALSE_INTERFACE 438 } 439 } 440 } 441 442 // On ES buffer mapping is an extension. On Desktop 443 // buffer mapping was part of original VBO extension 444 // which we require. 445 if (kGL_GrGLStandard == fStandard || fExtensions.has("GL_OES_mapbuffer")) { 446 if (NULL == fFunctions.fMapBuffer || 447 NULL == fFunctions.fUnmapBuffer) { 448 RETURN_FALSE_INTERFACE 449 } 450 } 451 452 // Dual source blending 453 if (kGL_GrGLStandard == fStandard && 454 (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_blend_func_extended"))) { 455 if (NULL == fFunctions.fBindFragDataLocationIndexed) { 456 RETURN_FALSE_INTERFACE 457 } 458 } 459 460 // glGetStringi was added in version 3.0 of both desktop and ES. 461 if (glVer >= GR_GL_VER(3, 0)) { 462 if (NULL == fFunctions.fGetStringi) { 463 RETURN_FALSE_INTERFACE 464 } 465 } 466 467 if (kGL_GrGLStandard == fStandard) { 468 if (glVer >= GR_GL_VER(3, 0) || fExtensions.has("GL_ARB_vertex_array_object")) { 469 if (NULL == fFunctions.fBindVertexArray || 470 NULL == fFunctions.fDeleteVertexArrays || 471 NULL == fFunctions.fGenVertexArrays) { 472 RETURN_FALSE_INTERFACE 473 } 474 } 475 } else { 476 if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_OES_vertex_array_object")) { 477 if (NULL == fFunctions.fBindVertexArray || 478 NULL == fFunctions.fDeleteVertexArrays || 479 NULL == fFunctions.fGenVertexArrays) { 480 RETURN_FALSE_INTERFACE 481 } 482 } 483 } 484 485 if (fExtensions.has("GL_EXT_debug_marker")) { 486 if (NULL == fFunctions.fInsertEventMarker || 487 NULL == fFunctions.fPushGroupMarker || 488 NULL == fFunctions.fPopGroupMarker) { 489 RETURN_FALSE_INTERFACE 490 } 491 } 492 493 if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,3)) || 494 fExtensions.has("GL_ARB_invalidate_subdata")) { 495 if (NULL == fFunctions.fInvalidateBufferData || 496 NULL == fFunctions.fInvalidateBufferSubData || 497 NULL == fFunctions.fInvalidateFramebuffer || 498 NULL == fFunctions.fInvalidateSubFramebuffer || 499 NULL == fFunctions.fInvalidateTexImage || 500 NULL == fFunctions.fInvalidateTexSubImage) { 501 RETURN_FALSE_INTERFACE; 502 } 503 } else if (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0)) { 504 // ES 3.0 adds the framebuffer functions but not the others. 505 if (NULL == fFunctions.fInvalidateFramebuffer || 506 NULL == fFunctions.fInvalidateSubFramebuffer) { 507 RETURN_FALSE_INTERFACE; 508 } 509 } 510 511 if (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_CHROMIUM_map_sub")) { 512 if (NULL == fFunctions.fMapBufferSubData || 513 NULL == fFunctions.fMapTexSubImage2D || 514 NULL == fFunctions.fUnmapBufferSubData || 515 NULL == fFunctions.fUnmapTexSubImage2D) { 516 RETURN_FALSE_INTERFACE; 517 } 518 } 519 520 // These functions are added to the 3.0 version of both GLES and GL. 521 if (glVer >= GR_GL_VER(3,0) || 522 (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_EXT_map_buffer_range")) || 523 (kGL_GrGLStandard == fStandard && fExtensions.has("GL_ARB_map_buffer_range"))) { 524 if (NULL == fFunctions.fMapBufferRange || 525 NULL == fFunctions.fFlushMappedBufferRange) { 526 RETURN_FALSE_INTERFACE; 527 } 528 } 529 return true; 530} 531