|
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 |
|
16 namespace { |
|
17 void GrGLDefaultInterfaceCallback(const GrGLInterface*) {} |
|
18 } |
|
19 #endif |
|
20 |
|
21 const 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 |
|
38 const 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 |
|
96 GrGLInterface::GrGLInterface() { |
|
97 fStandard = kNone_GrGLStandard; |
|
98 |
|
99 #if GR_GL_PER_GL_FUNC_CALLBACK |
|
100 fCallback = GrGLDefaultInterfaceCallback; |
|
101 fCallbackData = 0; |
|
102 #endif |
|
103 } |
|
104 |
|
105 GrGLInterface* 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 bool GrGLInterface::validate() const { |
|
120 |
|
121 if (kNone_GrGLStandard == fStandard) { |
|
122 return false; |
|
123 } |
|
124 |
|
125 if (!fExtensions.isInitialized()) { |
|
126 return false; |
|
127 } |
|
128 |
|
129 // functions that are always required |
|
130 if (NULL == fFunctions.fActiveTexture || |
|
131 NULL == fFunctions.fAttachShader || |
|
132 NULL == fFunctions.fBindAttribLocation || |
|
133 NULL == fFunctions.fBindBuffer || |
|
134 NULL == fFunctions.fBindTexture || |
|
135 NULL == fFunctions.fBlendFunc || |
|
136 NULL == fFunctions.fBlendColor || // -> GL >= 1.4, ES >= 2.0 or extension |
|
137 NULL == fFunctions.fBufferData || |
|
138 NULL == fFunctions.fBufferSubData || |
|
139 NULL == fFunctions.fClear || |
|
140 NULL == fFunctions.fClearColor || |
|
141 NULL == fFunctions.fClearStencil || |
|
142 NULL == fFunctions.fColorMask || |
|
143 NULL == fFunctions.fCompileShader || |
|
144 NULL == fFunctions.fCopyTexSubImage2D || |
|
145 NULL == fFunctions.fCreateProgram || |
|
146 NULL == fFunctions.fCreateShader || |
|
147 NULL == fFunctions.fCullFace || |
|
148 NULL == fFunctions.fDeleteBuffers || |
|
149 NULL == fFunctions.fDeleteProgram || |
|
150 NULL == fFunctions.fDeleteShader || |
|
151 NULL == fFunctions.fDeleteTextures || |
|
152 NULL == fFunctions.fDepthMask || |
|
153 NULL == fFunctions.fDisable || |
|
154 NULL == fFunctions.fDisableVertexAttribArray || |
|
155 NULL == fFunctions.fDrawArrays || |
|
156 NULL == fFunctions.fDrawElements || |
|
157 NULL == fFunctions.fEnable || |
|
158 NULL == fFunctions.fEnableVertexAttribArray || |
|
159 NULL == fFunctions.fFrontFace || |
|
160 NULL == fFunctions.fGenBuffers || |
|
161 NULL == fFunctions.fGenTextures || |
|
162 NULL == fFunctions.fGetBufferParameteriv || |
|
163 NULL == fFunctions.fGenerateMipmap || |
|
164 NULL == fFunctions.fGetError || |
|
165 NULL == fFunctions.fGetIntegerv || |
|
166 NULL == fFunctions.fGetProgramInfoLog || |
|
167 NULL == fFunctions.fGetProgramiv || |
|
168 NULL == fFunctions.fGetShaderInfoLog || |
|
169 NULL == fFunctions.fGetShaderiv || |
|
170 NULL == fFunctions.fGetString || |
|
171 NULL == fFunctions.fGetUniformLocation || |
|
172 NULL == fFunctions.fLinkProgram || |
|
173 NULL == fFunctions.fLineWidth || |
|
174 NULL == fFunctions.fPixelStorei || |
|
175 NULL == fFunctions.fReadPixels || |
|
176 NULL == fFunctions.fScissor || |
|
177 NULL == fFunctions.fShaderSource || |
|
178 NULL == fFunctions.fStencilFunc || |
|
179 NULL == fFunctions.fStencilMask || |
|
180 NULL == fFunctions.fStencilOp || |
|
181 NULL == fFunctions.fTexImage2D || |
|
182 NULL == fFunctions.fTexParameteri || |
|
183 NULL == fFunctions.fTexParameteriv || |
|
184 NULL == fFunctions.fTexSubImage2D || |
|
185 NULL == fFunctions.fUniform1f || |
|
186 NULL == fFunctions.fUniform1i || |
|
187 NULL == fFunctions.fUniform1fv || |
|
188 NULL == fFunctions.fUniform1iv || |
|
189 NULL == fFunctions.fUniform2f || |
|
190 NULL == fFunctions.fUniform2i || |
|
191 NULL == fFunctions.fUniform2fv || |
|
192 NULL == fFunctions.fUniform2iv || |
|
193 NULL == fFunctions.fUniform3f || |
|
194 NULL == fFunctions.fUniform3i || |
|
195 NULL == fFunctions.fUniform3fv || |
|
196 NULL == fFunctions.fUniform3iv || |
|
197 NULL == fFunctions.fUniform4f || |
|
198 NULL == fFunctions.fUniform4i || |
|
199 NULL == fFunctions.fUniform4fv || |
|
200 NULL == fFunctions.fUniform4iv || |
|
201 NULL == fFunctions.fUniformMatrix2fv || |
|
202 NULL == fFunctions.fUniformMatrix3fv || |
|
203 NULL == fFunctions.fUniformMatrix4fv || |
|
204 NULL == fFunctions.fUseProgram || |
|
205 NULL == fFunctions.fVertexAttrib4fv || |
|
206 NULL == fFunctions.fVertexAttribPointer || |
|
207 NULL == fFunctions.fViewport || |
|
208 NULL == fFunctions.fBindFramebuffer || |
|
209 NULL == fFunctions.fBindRenderbuffer || |
|
210 NULL == fFunctions.fCheckFramebufferStatus || |
|
211 NULL == fFunctions.fDeleteFramebuffers || |
|
212 NULL == fFunctions.fDeleteRenderbuffers || |
|
213 NULL == fFunctions.fFinish || |
|
214 NULL == fFunctions.fFlush || |
|
215 NULL == fFunctions.fFramebufferRenderbuffer || |
|
216 NULL == fFunctions.fFramebufferTexture2D || |
|
217 NULL == fFunctions.fGetFramebufferAttachmentParameteriv || |
|
218 NULL == fFunctions.fGetRenderbufferParameteriv || |
|
219 NULL == fFunctions.fGenFramebuffers || |
|
220 NULL == fFunctions.fGenRenderbuffers || |
|
221 NULL == fFunctions.fRenderbufferStorage) { |
|
222 return false; |
|
223 } |
|
224 |
|
225 GrGLVersion glVer = GrGLGetVersion(this); |
|
226 |
|
227 bool isCoreProfile = false; |
|
228 if (kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,2)) { |
|
229 GrGLint profileMask; |
|
230 GR_GL_GetIntegerv(this, GR_GL_CONTEXT_PROFILE_MASK, &profileMask); |
|
231 isCoreProfile = SkToBool(profileMask & GR_GL_CONTEXT_CORE_PROFILE_BIT); |
|
232 } |
|
233 |
|
234 // Now check that baseline ES/Desktop fns not covered above are present |
|
235 // and that we have fn pointers for any advertised fExtensions that we will |
|
236 // try to use. |
|
237 |
|
238 // these functions are part of ES2, we assume they are available |
|
239 // On the desktop we assume they are available if the extension |
|
240 // is present or GL version is high enough. |
|
241 if (kGLES_GrGLStandard == fStandard) { |
|
242 if (NULL == fFunctions.fStencilFuncSeparate || |
|
243 NULL == fFunctions.fStencilMaskSeparate || |
|
244 NULL == fFunctions.fStencilOpSeparate) { |
|
245 return false; |
|
246 } |
|
247 } else if (kGL_GrGLStandard == fStandard) { |
|
248 |
|
249 if (glVer >= GR_GL_VER(2,0)) { |
|
250 if (NULL == fFunctions.fStencilFuncSeparate || |
|
251 NULL == fFunctions.fStencilMaskSeparate || |
|
252 NULL == fFunctions.fStencilOpSeparate) { |
|
253 return false; |
|
254 } |
|
255 } |
|
256 if (glVer >= GR_GL_VER(3,0) && NULL == fFunctions.fBindFragDataLocation) { |
|
257 return false; |
|
258 } |
|
259 if (glVer >= GR_GL_VER(2,0) || fExtensions.has("GL_ARB_draw_buffers")) { |
|
260 if (NULL == fFunctions.fDrawBuffers) { |
|
261 return false; |
|
262 } |
|
263 } |
|
264 |
|
265 if (glVer >= GR_GL_VER(1,5) || fExtensions.has("GL_ARB_occlusion_query")) { |
|
266 if (NULL == fFunctions.fGenQueries || |
|
267 NULL == fFunctions.fDeleteQueries || |
|
268 NULL == fFunctions.fBeginQuery || |
|
269 NULL == fFunctions.fEndQuery || |
|
270 NULL == fFunctions.fGetQueryiv || |
|
271 NULL == fFunctions.fGetQueryObjectiv || |
|
272 NULL == fFunctions.fGetQueryObjectuiv) { |
|
273 return false; |
|
274 } |
|
275 } |
|
276 if (glVer >= GR_GL_VER(3,3) || |
|
277 fExtensions.has("GL_ARB_timer_query") || |
|
278 fExtensions.has("GL_EXT_timer_query")) { |
|
279 if (NULL == fFunctions.fGetQueryObjecti64v || |
|
280 NULL == fFunctions.fGetQueryObjectui64v) { |
|
281 return false; |
|
282 } |
|
283 } |
|
284 if (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_timer_query")) { |
|
285 if (NULL == fFunctions.fQueryCounter) { |
|
286 return false; |
|
287 } |
|
288 } |
|
289 if (!isCoreProfile) { |
|
290 if (NULL == fFunctions.fLoadIdentity || |
|
291 NULL == fFunctions.fLoadMatrixf || |
|
292 NULL == fFunctions.fMatrixMode || |
|
293 NULL == fFunctions.fTexGenfv || |
|
294 NULL == fFunctions.fTexGeni) { |
|
295 return false; |
|
296 } |
|
297 } |
|
298 if (fExtensions.has("GL_NV_path_rendering")) { |
|
299 if (NULL == fFunctions.fPathCommands || |
|
300 NULL == fFunctions.fPathCoords || |
|
301 NULL == fFunctions.fPathSubCommands || |
|
302 NULL == fFunctions.fPathSubCoords || |
|
303 NULL == fFunctions.fPathString || |
|
304 NULL == fFunctions.fPathGlyphs || |
|
305 NULL == fFunctions.fPathGlyphRange || |
|
306 NULL == fFunctions.fWeightPaths || |
|
307 NULL == fFunctions.fCopyPath || |
|
308 NULL == fFunctions.fInterpolatePaths || |
|
309 NULL == fFunctions.fTransformPath || |
|
310 NULL == fFunctions.fPathParameteriv || |
|
311 NULL == fFunctions.fPathParameteri || |
|
312 NULL == fFunctions.fPathParameterfv || |
|
313 NULL == fFunctions.fPathParameterf || |
|
314 NULL == fFunctions.fPathDashArray || |
|
315 NULL == fFunctions.fGenPaths || |
|
316 NULL == fFunctions.fDeletePaths || |
|
317 NULL == fFunctions.fIsPath || |
|
318 NULL == fFunctions.fPathStencilFunc || |
|
319 NULL == fFunctions.fPathStencilDepthOffset || |
|
320 NULL == fFunctions.fStencilFillPath || |
|
321 NULL == fFunctions.fStencilStrokePath || |
|
322 NULL == fFunctions.fStencilFillPathInstanced || |
|
323 NULL == fFunctions.fStencilStrokePathInstanced || |
|
324 NULL == fFunctions.fPathCoverDepthFunc || |
|
325 NULL == fFunctions.fPathColorGen || |
|
326 NULL == fFunctions.fPathTexGen || |
|
327 NULL == fFunctions.fPathFogGen || |
|
328 NULL == fFunctions.fCoverFillPath || |
|
329 NULL == fFunctions.fCoverStrokePath || |
|
330 NULL == fFunctions.fCoverFillPathInstanced || |
|
331 NULL == fFunctions.fCoverStrokePathInstanced || |
|
332 NULL == fFunctions.fGetPathParameteriv || |
|
333 NULL == fFunctions.fGetPathParameterfv || |
|
334 NULL == fFunctions.fGetPathCommands || |
|
335 NULL == fFunctions.fGetPathCoords || |
|
336 NULL == fFunctions.fGetPathDashArray || |
|
337 NULL == fFunctions.fGetPathMetrics || |
|
338 NULL == fFunctions.fGetPathMetricRange || |
|
339 NULL == fFunctions.fGetPathSpacing || |
|
340 NULL == fFunctions.fGetPathColorGeniv || |
|
341 NULL == fFunctions.fGetPathColorGenfv || |
|
342 NULL == fFunctions.fGetPathTexGeniv || |
|
343 NULL == fFunctions.fGetPathTexGenfv || |
|
344 NULL == fFunctions.fIsPointInFillPath || |
|
345 NULL == fFunctions.fIsPointInStrokePath || |
|
346 NULL == fFunctions.fGetPathLength || |
|
347 NULL == fFunctions.fPointAlongPath) { |
|
348 return false; |
|
349 } |
|
350 } |
|
351 } |
|
352 |
|
353 // optional function on desktop before 1.3 |
|
354 if (kGL_GrGLStandard != fStandard || |
|
355 (glVer >= GR_GL_VER(1,3)) || |
|
356 fExtensions.has("GL_ARB_texture_compression")) { |
|
357 if (NULL == fFunctions.fCompressedTexImage2D) { |
|
358 return false; |
|
359 } |
|
360 } |
|
361 |
|
362 // part of desktop GL, but not ES |
|
363 if (kGL_GrGLStandard == fStandard && |
|
364 (NULL == fFunctions.fGetTexLevelParameteriv || |
|
365 NULL == fFunctions.fDrawBuffer || |
|
366 NULL == fFunctions.fReadBuffer)) { |
|
367 return false; |
|
368 } |
|
369 |
|
370 // GL_EXT_texture_storage is part of desktop 4.2 |
|
371 // There is a desktop ARB extension and an ES+desktop EXT extension |
|
372 if (kGL_GrGLStandard == fStandard) { |
|
373 if (glVer >= GR_GL_VER(4,2) || |
|
374 fExtensions.has("GL_ARB_texture_storage") || |
|
375 fExtensions.has("GL_EXT_texture_storage")) { |
|
376 if (NULL == fFunctions.fTexStorage2D) { |
|
377 return false; |
|
378 } |
|
379 } |
|
380 } else if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_EXT_texture_storage")) { |
|
381 if (NULL == fFunctions.fTexStorage2D) { |
|
382 return false; |
|
383 } |
|
384 } |
|
385 |
|
386 if (fExtensions.has("GL_EXT_discard_framebuffer")) { |
|
387 // FIXME: Remove this once Chromium is updated to provide this function |
|
388 #if 0 |
|
389 if (NULL == fFunctions.fDiscardFramebuffer) { |
|
390 return false; |
|
391 } |
|
392 #endif |
|
393 } |
|
394 |
|
395 // FBO MSAA |
|
396 if (kGL_GrGLStandard == fStandard) { |
|
397 // GL 3.0 and the ARB extension have multisample + blit |
|
398 if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_ARB_framebuffer_object")) { |
|
399 if (NULL == fFunctions.fRenderbufferStorageMultisample || |
|
400 NULL == fFunctions.fBlitFramebuffer) { |
|
401 return false; |
|
402 } |
|
403 } else { |
|
404 if (fExtensions.has("GL_EXT_framebuffer_blit") && |
|
405 NULL == fFunctions.fBlitFramebuffer) { |
|
406 return false; |
|
407 } |
|
408 if (fExtensions.has("GL_EXT_framebuffer_multisample") && |
|
409 NULL == fFunctions.fRenderbufferStorageMultisample) { |
|
410 return false; |
|
411 } |
|
412 } |
|
413 } else { |
|
414 if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_CHROMIUM_framebuffer_multisample")) { |
|
415 if (NULL == fFunctions.fRenderbufferStorageMultisample || |
|
416 NULL == fFunctions.fBlitFramebuffer) { |
|
417 return false; |
|
418 } |
|
419 } |
|
420 if (fExtensions.has("GL_APPLE_framebuffer_multisample")) { |
|
421 if (NULL == fFunctions.fRenderbufferStorageMultisampleES2APPLE || |
|
422 NULL == fFunctions.fResolveMultisampleFramebuffer) { |
|
423 return false; |
|
424 } |
|
425 } |
|
426 if (fExtensions.has("GL_IMG_multisampled_render_to_texture") || |
|
427 fExtensions.has("GL_EXT_multisampled_render_to_texture")) { |
|
428 if (NULL == fFunctions.fRenderbufferStorageMultisampleES2EXT || |
|
429 NULL == fFunctions.fFramebufferTexture2DMultisample) { |
|
430 return false; |
|
431 } |
|
432 } |
|
433 } |
|
434 |
|
435 // On ES buffer mapping is an extension. On Desktop |
|
436 // buffer mapping was part of original VBO extension |
|
437 // which we require. |
|
438 if (kGL_GrGLStandard == fStandard || fExtensions.has("GL_OES_mapbuffer")) { |
|
439 if (NULL == fFunctions.fMapBuffer || |
|
440 NULL == fFunctions.fUnmapBuffer) { |
|
441 return false; |
|
442 } |
|
443 } |
|
444 |
|
445 // Dual source blending |
|
446 if (kGL_GrGLStandard == fStandard && |
|
447 (glVer >= GR_GL_VER(3,3) || fExtensions.has("GL_ARB_blend_func_extended"))) { |
|
448 if (NULL == fFunctions.fBindFragDataLocationIndexed) { |
|
449 return false; |
|
450 } |
|
451 } |
|
452 |
|
453 // glGetStringi was added in version 3.0 of both desktop and ES. |
|
454 if (glVer >= GR_GL_VER(3, 0)) { |
|
455 if (NULL == fFunctions.fGetStringi) { |
|
456 return false; |
|
457 } |
|
458 } |
|
459 |
|
460 if (kGL_GrGLStandard == fStandard) { |
|
461 if (glVer >= GR_GL_VER(3, 0) || fExtensions.has("GL_ARB_vertex_array_object")) { |
|
462 if (NULL == fFunctions.fBindVertexArray || |
|
463 NULL == fFunctions.fDeleteVertexArrays || |
|
464 NULL == fFunctions.fGenVertexArrays) { |
|
465 return false; |
|
466 } |
|
467 } |
|
468 } else { |
|
469 if (glVer >= GR_GL_VER(3,0) || fExtensions.has("GL_OES_vertex_array_object")) { |
|
470 if (NULL == fFunctions.fBindVertexArray || |
|
471 NULL == fFunctions.fDeleteVertexArrays || |
|
472 NULL == fFunctions.fGenVertexArrays) { |
|
473 return false; |
|
474 } |
|
475 } |
|
476 } |
|
477 |
|
478 #if 0 |
|
479 if (fExtensions.has("GL_EXT_debug_marker")) { |
|
480 if (NULL == fFunctions.fInsertEventMarker || |
|
481 NULL == fFunctions.fPushGroupMarker || |
|
482 NULL == fFunctions.fPopGroupMarker) { |
|
483 return false; |
|
484 } |
|
485 } |
|
486 #endif |
|
487 return true; |
|
488 } |