• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #ifndef _GL_CLIENT_STATE_H_
17 #define _GL_CLIENT_STATE_H_
18 
19 #define GL_API
20 #ifndef ANDROID
21 #define GL_APIENTRY
22 #define GL_APIENTRYP
23 #endif
24 
25 #include <GLES/gl.h>
26 #include <GLES/glext.h>
27 #include <GLES2/gl2.h>
28 #include <GLES2/gl2ext.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include "ErrorLog.h"
33 #include "codec_defs.h"
34 
35 #include <vector>
36 #include <set>
37 
38 // Tracking framebuffer objects:
39 // which framebuffer is bound,
40 // and which texture names
41 // are currently bound to which attachment points.
42 struct FboProps {
43     GLenum target;
44     GLuint name;
45     bool previouslyBound;
46     GLuint colorAttachment0_texture;
47     GLuint depthAttachment_texture;
48     GLuint stencilAttachment_texture;
49 
50     bool colorAttachment0_hasTexObj;
51     bool depthAttachment_hasTexObj;
52     bool stencilAttachment_hasTexObj;
53 
54     GLuint colorAttachment0_rbo;
55     GLuint depthAttachment_rbo;
56     GLuint stencilAttachment_rbo;
57 
58     bool colorAttachment0_hasRbo;
59     bool depthAttachment_hasRbo;
60     bool stencilAttachment_hasRbo;
61 };
62 
63 // Same for Rbo's
64 struct RboProps {
65     GLenum target;
66     GLuint name;
67     GLenum format;
68     bool previouslyBound;
69 };
70 
71 // Enum for describing whether a framebuffer attachment
72 // is a texture or renderbuffer.
73 enum FboAttachmentType {
74     FBO_ATTACHMENT_RENDERBUFFER = 0,
75     FBO_ATTACHMENT_TEXTURE = 1,
76     FBO_ATTACHMENT_NONE = 2
77 };
78 
79 // Tracking FBO format
80 struct FboFormatInfo {
81     FboAttachmentType type;
82     GLenum rb_format;
83     GLint tex_internalformat;
84     GLenum tex_format;
85     GLenum tex_type;
86 };
87 
88 class GLClientState {
89 public:
90     typedef enum {
91         VERTEX_LOCATION = 0,
92         NORMAL_LOCATION = 1,
93         COLOR_LOCATION = 2,
94         POINTSIZE_LOCATION = 3,
95         TEXCOORD0_LOCATION = 4,
96         TEXCOORD1_LOCATION = 5,
97         TEXCOORD2_LOCATION = 6,
98         TEXCOORD3_LOCATION = 7,
99         TEXCOORD4_LOCATION = 8,
100         TEXCOORD5_LOCATION = 9,
101         TEXCOORD6_LOCATION = 10,
102         TEXCOORD7_LOCATION = 11,
103         MATRIXINDEX_LOCATION = 12,
104         WEIGHT_LOCATION = 13,
105         LAST_LOCATION = 14
106     } StateLocation;
107 
108     typedef struct {
109         GLint enabled;
110         GLint size;
111         GLenum type;
112         GLsizei stride;
113         void *data;
114         GLuint bufferObject;
115         GLenum glConst;
116         unsigned int elementSize;
117         bool enableDirty;  // true if any enable state has changed since last draw
118         bool normalized;
119     } VertexAttribState;
120 
121     typedef struct {
122         int unpack_alignment;
123         int pack_alignment;
124     } PixelStoreState;
125 
126     enum {
127         MAX_TEXTURE_UNITS = 32,
128     };
129 
130 public:
131     GLClientState(int nLocations = CODEC_MAX_VERTEX_ATTRIBUTES);
132     ~GLClientState();
nLocations()133     int nLocations() { return m_nLocations; }
pixelStoreState()134     const PixelStoreState *pixelStoreState() { return &m_pixelStore; }
135     int setPixelStore(GLenum param, GLint value);
currentArrayVbo()136     GLuint currentArrayVbo() { return m_currentArrayVbo; }
currentIndexVbo()137     GLuint currentIndexVbo() { return m_currentIndexVbo; }
138     void enable(int location, int state);
139     void setState(int  location, int size, GLenum type, GLboolean normalized, GLsizei stride, const void *data);
140     void setBufferObject(int location, GLuint id);
141     const VertexAttribState  *getState(int location);
142     const VertexAttribState  *getStateAndEnableDirty(int location, bool *enableChanged);
143     int getLocation(GLenum loc);
setActiveTexture(int texUnit)144     void setActiveTexture(int texUnit) {m_activeTexture = texUnit; };
getActiveTexture()145     int getActiveTexture() const { return m_activeTexture; }
setMaxVertexAttribs(int val)146     void setMaxVertexAttribs(int val) {
147         m_maxVertexAttribs = val;
148         m_maxVertexAttribsDirty = false;
149     }
150 
unBindBuffer(GLuint id)151     void unBindBuffer(GLuint id)
152     {
153         if (m_currentArrayVbo == id) m_currentArrayVbo = 0;
154         else if (m_currentIndexVbo == id) m_currentIndexVbo = 0;
155     }
156 
bindBuffer(GLenum target,GLuint id)157     int bindBuffer(GLenum target, GLuint id)
158     {
159         int err = 0;
160         switch(target) {
161         case GL_ARRAY_BUFFER:
162             m_currentArrayVbo = id;
163             break;
164         case GL_ELEMENT_ARRAY_BUFFER:
165             m_currentIndexVbo = id;
166             break;
167         default:
168             err = -1;
169         }
170         return err;
171     }
172 
getBuffer(GLenum target)173     int getBuffer(GLenum target)
174     {
175       int ret=0;
176       switch (target) {
177       case GL_ARRAY_BUFFER:
178           ret = m_currentArrayVbo;
179           break;
180       case GL_ELEMENT_ARRAY_BUFFER:
181           ret = m_currentIndexVbo;
182           break;
183       default:
184           ret = -1;
185       }
186       return ret;
187     }
188     size_t pixelDataSize(GLsizei width, GLsizei height, GLenum format, GLenum type, int pack) const;
189 
setCurrentProgram(GLint program)190     void setCurrentProgram(GLint program) { m_currentProgram = program; }
currentProgram()191     GLint currentProgram() const { return m_currentProgram; }
192 
193     /* OES_EGL_image_external
194      *
195      * These functions manipulate GL state which interacts with the
196      * OES_EGL_image_external extension, to support client-side emulation on
197      * top of host implementations that don't have it.
198      *
199      * Most of these calls should only be used with TEXTURE_2D or
200      * TEXTURE_EXTERNAL_OES texture targets; TEXTURE_CUBE_MAP or other extension
201      * targets should bypass this. An exception is bindTexture(), which should
202      * see all glBindTexture() calls for any target.
203      */
204 
205     // glActiveTexture(GL_TEXTURE0 + i)
206     // Sets the active texture unit. Up to MAX_TEXTURE_UNITS are supported.
207     GLenum setActiveTextureUnit(GLenum texture);
208     GLenum getActiveTextureUnit() const;
209 
210     // glEnable(GL_TEXTURE_(2D|EXTERNAL_OES))
211     void enableTextureTarget(GLenum target);
212 
213     // glDisable(GL_TEXTURE_(2D|EXTERNAL_OES))
214     void disableTextureTarget(GLenum target);
215 
216     // Implements the target priority logic:
217     // * Return GL_TEXTURE_EXTERNAL_OES if enabled, else
218     // * Return GL_TEXTURE_2D if enabled, else
219     // * Return the allDisabled value.
220     // For some cases passing GL_TEXTURE_2D for allDisabled makes callee code
221     // simpler; for other cases passing a recognizable enum like GL_ZERO or
222     // GL_INVALID_ENUM is appropriate.
223     GLenum getPriorityEnabledTarget(GLenum allDisabled) const;
224 
225     // glBindTexture(GL_TEXTURE_*, ...)
226     // Set the target binding of the active texture unit to texture. Returns
227     // GL_NO_ERROR on success or GL_INVALID_OPERATION if the texture has
228     // previously been bound to a different target. If firstUse is not NULL,
229     // it is set to indicate whether this is the first use of the texture.
230     // For accurate error detection, bindTexture should be called for *all*
231     // targets, not just 2D and EXTERNAL_OES.
232     GLenum bindTexture(GLenum target, GLuint texture, GLboolean* firstUse);
233 
234     // Return the texture currently bound to GL_TEXTURE_(2D|EXTERNAL_OES).
235     GLuint getBoundTexture(GLenum target) const;
236 
237     // For AMD GPUs, it is easy for the emulator to segfault
238     // (esp. in dEQP) when a cube map is defined using glCopyTexImage2D
239     // and uses GL_LUMINANCE as internal format.
240     // In particular, the segfault happens when negative components of
241     // cube maps are defined before positive ones,
242     // This procedure checks internal state to see if we have defined
243     // the positive component of a cube map already. If not, it returns
244     // which positive component needs to be defined first.
245     // If there is no need for the extra definition, 0 is returned.
246     GLenum copyTexImageLuminanceCubeMapAMDWorkaround(GLenum target, GLint level,
247                                                      GLenum internalformat);
248 
249     // Tracks the format of the currently bound texture.
250     // This is to pass dEQP tests for fbo completeness.
251     void setBoundTextureInternalFormat(GLenum target, GLint format);
252     void setBoundTextureFormat(GLenum target, GLenum format);
253     void setBoundTextureType(GLenum target, GLenum type);
254 
255     // glDeleteTextures(...)
256     // Remove references to the to-be-deleted textures.
257     void deleteTextures(GLsizei n, const GLuint* textures);
258 
259     // Render buffer objects
260     void addRenderbuffers(GLsizei n, GLuint* renderbuffers);
261     void removeRenderbuffers(GLsizei n, const GLuint* renderbuffers);
262     bool usedRenderbufferName(GLuint name) const;
263     void bindRenderbuffer(GLenum target, GLuint name);
264     GLuint boundRenderbuffer() const;
265     void setBoundRenderbufferFormat(GLenum format);
266 
267     // Frame buffer objects
268     void addFramebuffers(GLsizei n, GLuint* framebuffers);
269     void removeFramebuffers(GLsizei n, const GLuint* framebuffers);
270     bool usedFramebufferName(GLuint name) const;
271     void bindFramebuffer(GLenum target, GLuint name);
272     void setCheckFramebufferStatus(GLenum status);
273     GLenum getCheckFramebufferStatus() const;
274     GLuint boundFramebuffer() const;
275 
276     // Texture object -> FBO
277     void attachTextureObject(GLenum attachment, GLuint texture);
278     GLuint getFboAttachmentTextureId(GLenum attachment) const;
279 
280     // RBO -> FBO
281     void attachRbo(GLenum attachment, GLuint renderbuffer);
282     GLuint getFboAttachmentRboId(GLenum attachment) const;
283 
284     // FBO attachments in general
285     bool attachmentHasObject(GLenum attachment) const;
286 
287     // set eglsurface property on default framebuffer
288     // if coming from eglMakeCurrent
289     void fromMakeCurrent();
290 
291     // Queries the format backing the current framebuffer.
292     // Type differs depending on whether the attachment
293     // is a texture or renderbuffer.
294     void getBoundFramebufferFormat(
295             GLenum attachment, FboFormatInfo* res_info) const;
296 
297 private:
298     PixelStoreState m_pixelStore;
299     VertexAttribState *m_states;
300     int m_maxVertexAttribs;
301     bool m_maxVertexAttribsDirty;
302     int m_nLocations;
303     GLuint m_currentArrayVbo;
304     GLuint m_currentIndexVbo;
305     int m_activeTexture;
306     GLint m_currentProgram;
307 
validLocation(int location)308     bool validLocation(int location) { return (location >= 0 && location < m_nLocations); }
309 
310     enum TextureTarget {
311         TEXTURE_2D = 0,
312         TEXTURE_EXTERNAL = 1,
313         TEXTURE_TARGET_COUNT
314     };
315     struct TextureUnit {
316         unsigned int enables;
317         GLuint texture[TEXTURE_TARGET_COUNT];
318     };
319     struct TextureRec {
320         GLuint id;
321         GLenum target;
322         GLint internalformat;
323         GLenum format;
324         GLenum type;
325     };
326     struct TextureState {
327         TextureUnit unit[MAX_TEXTURE_UNITS];
328         TextureUnit* activeUnit;
329         TextureRec* textures;
330         GLuint numTextures;
331         GLuint allocTextures;
332     };
333     TextureState m_tex;
334 
335     // State tracking of cube map definitions.
336     // Currently used only for driver workarounds
337     // when using GL_LUMINANCE and defining cube maps with
338     // glCopyTexImage2D.
339     struct CubeMapDef {
340         GLuint id;
341         GLenum target;
342         GLint level;
343         GLenum internalformat;
344     };
345     struct CubeMapDefCompare {
operatorCubeMapDefCompare346         bool operator() (const CubeMapDef& a,
347                          const CubeMapDef& b) const {
348             if (a.id != b.id) return a.id < b.id;
349             if (a.target != b.target) return a.target < b.target;
350             if (a.level != b.level) return a.level < b.level;
351             if (a.internalformat != b.internalformat)
352                 return a.internalformat < b.internalformat;
353             return false;
354         }
355     };
356     std::set<CubeMapDef, CubeMapDefCompare> m_cubeMapDefs;
357     void writeCopyTexImageState(GLenum target, GLint level,
358                                 GLenum internalformat);
359     GLenum copyTexImageNeededTarget(GLenum target, GLint level,
360                                     GLenum internalformat);
361 
362     struct RboState {
363         GLuint boundRenderbuffer;
364         size_t boundRenderbufferIndex;
365         std::vector<RboProps> rboData;
366     };
367     RboState mRboState;
368     void addFreshRenderbuffer(GLuint name);
369     void setBoundRenderbufferIndex();
370     size_t getRboIndex(GLuint name) const;
371     RboProps& boundRboProps();
372     const RboProps& boundRboProps_const() const;
373 
374     struct FboState {
375         GLuint boundFramebuffer;
376         size_t boundFramebufferIndex;
377         std::vector<FboProps> fboData;
378         GLenum fboCheckStatus;
379     };
380     FboState mFboState;
381     void addFreshFramebuffer(GLuint name);
382     void setBoundFramebufferIndex();
383     size_t getFboIndex(GLuint name) const;
384     FboProps& boundFboProps();
385     const FboProps& boundFboProps_const() const;
386 
387     // Querying framebuffer format
388     GLenum queryRboFormat(GLuint name) const;
389     GLint queryTexInternalFormat(GLuint name) const;
390     GLenum queryTexFormat(GLuint name) const;
391     GLenum queryTexType(GLuint name) const;
392 
393     static int compareTexId(const void* pid, const void* prec);
394     TextureRec* addTextureRec(GLuint id, GLenum target);
395 
396 public:
397     void getClientStatePointer(GLenum pname, GLvoid** params);
398 
399     template <class T>
getVertexAttribParameter(GLuint index,GLenum param,T * ptr)400     int getVertexAttribParameter(GLuint index, GLenum param, T *ptr)
401     {
402         bool handled = true;
403         const VertexAttribState *vertexAttrib = getState(index);
404         if (vertexAttrib == NULL) {
405             ERR("getVeterxAttriParameter for non existant index %d\n", index);
406             // set gl error;
407             return handled;
408         }
409 
410         switch(param) {
411         case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
412             *ptr = (T)(vertexAttrib->bufferObject);
413             break;
414         case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
415             *ptr = (T)(vertexAttrib->enabled);
416             break;
417         case GL_VERTEX_ATTRIB_ARRAY_SIZE:
418             *ptr = (T)(vertexAttrib->size);
419             break;
420         case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
421             *ptr = (T)(vertexAttrib->stride);
422             break;
423         case GL_VERTEX_ATTRIB_ARRAY_TYPE:
424             *ptr = (T)(vertexAttrib->type);
425             break;
426         case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
427             *ptr = (T)(vertexAttrib->normalized);
428             break;
429         case GL_CURRENT_VERTEX_ATTRIB:
430             handled = false;
431             break;
432         default:
433             handled = false;
434             ERR("unknown vertex-attrib parameter param %d\n", param);
435         }
436         return handled;
437     }
438 
439     template <class T>
getClientStateParameter(GLenum param,T * ptr)440     bool getClientStateParameter(GLenum param, T* ptr)
441     {
442         bool isClientStateParam = false;
443         switch (param) {
444         case GL_CLIENT_ACTIVE_TEXTURE: {
445             GLint tex = getActiveTexture() + GL_TEXTURE0;
446             *ptr = tex;
447             isClientStateParam = true;
448             break;
449             }
450         case GL_VERTEX_ARRAY_SIZE: {
451             const GLClientState::VertexAttribState *state = getState(GLClientState::VERTEX_LOCATION);
452             *ptr = state->size;
453             isClientStateParam = true;
454             break;
455             }
456         case GL_VERTEX_ARRAY_TYPE: {
457             const GLClientState::VertexAttribState *state = getState(GLClientState::VERTEX_LOCATION);
458             *ptr = state->type;
459             isClientStateParam = true;
460             break;
461             }
462         case GL_VERTEX_ARRAY_STRIDE: {
463             const GLClientState::VertexAttribState *state = getState(GLClientState::VERTEX_LOCATION);
464             *ptr = state->stride;
465             isClientStateParam = true;
466             break;
467             }
468         case GL_COLOR_ARRAY_SIZE: {
469             const GLClientState::VertexAttribState *state = getState(GLClientState::COLOR_LOCATION);
470             *ptr = state->size;
471             isClientStateParam = true;
472             break;
473             }
474         case GL_COLOR_ARRAY_TYPE: {
475             const GLClientState::VertexAttribState *state = getState(GLClientState::COLOR_LOCATION);
476             *ptr = state->type;
477             isClientStateParam = true;
478             break;
479             }
480         case GL_COLOR_ARRAY_STRIDE: {
481             const GLClientState::VertexAttribState *state = getState(GLClientState::COLOR_LOCATION);
482             *ptr = state->stride;
483             isClientStateParam = true;
484             break;
485             }
486         case GL_NORMAL_ARRAY_TYPE: {
487             const GLClientState::VertexAttribState *state = getState(GLClientState::NORMAL_LOCATION);
488             *ptr = state->type;
489             isClientStateParam = true;
490             break;
491             }
492         case GL_NORMAL_ARRAY_STRIDE: {
493             const GLClientState::VertexAttribState *state = getState(GLClientState::NORMAL_LOCATION);
494             *ptr = state->stride;
495             isClientStateParam = true;
496             break;
497             }
498         case GL_TEXTURE_COORD_ARRAY_SIZE: {
499             const GLClientState::VertexAttribState *state = getState(getActiveTexture() + GLClientState::TEXCOORD0_LOCATION);
500             *ptr = state->size;
501             isClientStateParam = true;
502             break;
503             }
504         case GL_TEXTURE_COORD_ARRAY_TYPE: {
505             const GLClientState::VertexAttribState *state = getState(getActiveTexture() + GLClientState::TEXCOORD0_LOCATION);
506             *ptr = state->type;
507             isClientStateParam = true;
508             break;
509             }
510         case GL_TEXTURE_COORD_ARRAY_STRIDE: {
511             const GLClientState::VertexAttribState *state = getState(getActiveTexture() + GLClientState::TEXCOORD0_LOCATION);
512             *ptr = state->stride;
513             isClientStateParam = true;
514             break;
515             }
516         case GL_POINT_SIZE_ARRAY_TYPE_OES: {
517             const GLClientState::VertexAttribState *state = getState(GLClientState::POINTSIZE_LOCATION);
518             *ptr = state->type;
519             isClientStateParam = true;
520             break;
521             }
522         case GL_POINT_SIZE_ARRAY_STRIDE_OES: {
523             const GLClientState::VertexAttribState *state = getState(GLClientState::POINTSIZE_LOCATION);
524             *ptr = state->stride;
525             isClientStateParam = true;
526             break;
527             }
528         case GL_MATRIX_INDEX_ARRAY_SIZE_OES: {
529             const GLClientState::VertexAttribState *state = getState(GLClientState::MATRIXINDEX_LOCATION);
530             *ptr = state->size;
531             isClientStateParam = true;
532             break;
533             }
534         case GL_MATRIX_INDEX_ARRAY_TYPE_OES: {
535             const GLClientState::VertexAttribState *state = getState(GLClientState::MATRIXINDEX_LOCATION);
536             *ptr = state->type;
537             isClientStateParam = true;
538             break;
539             }
540         case GL_MATRIX_INDEX_ARRAY_STRIDE_OES: {
541             const GLClientState::VertexAttribState *state = getState(GLClientState::MATRIXINDEX_LOCATION);
542             *ptr = state->stride;
543             isClientStateParam = true;
544             break;
545             }
546         case GL_WEIGHT_ARRAY_SIZE_OES: {
547             const GLClientState::VertexAttribState *state = getState(GLClientState::WEIGHT_LOCATION);
548             *ptr = state->size;
549             isClientStateParam = true;
550             break;
551             }
552         case GL_WEIGHT_ARRAY_TYPE_OES: {
553             const GLClientState::VertexAttribState *state = getState(GLClientState::WEIGHT_LOCATION);
554             *ptr = state->type;
555             isClientStateParam = true;
556             break;
557             }
558         case GL_WEIGHT_ARRAY_STRIDE_OES: {
559             const GLClientState::VertexAttribState *state = getState(GLClientState::WEIGHT_LOCATION);
560             *ptr = state->stride;
561             isClientStateParam = true;
562             break;
563             }
564         case GL_VERTEX_ARRAY_BUFFER_BINDING: {
565             const GLClientState::VertexAttribState *state = getState(GLClientState::VERTEX_LOCATION);
566             *ptr = state->bufferObject;
567             isClientStateParam = true;
568             break;
569             }
570         case GL_NORMAL_ARRAY_BUFFER_BINDING: {
571             const GLClientState::VertexAttribState *state = getState(GLClientState::NORMAL_LOCATION);
572             *ptr = state->bufferObject;
573             isClientStateParam = true;
574             break;
575             }
576         case GL_COLOR_ARRAY_BUFFER_BINDING: {
577             const GLClientState::VertexAttribState *state = getState(GLClientState::COLOR_LOCATION);
578             *ptr = state->bufferObject;
579             isClientStateParam = true;
580             break;
581             }
582         case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: {
583             const GLClientState::VertexAttribState *state = getState(getActiveTexture()+GLClientState::TEXCOORD0_LOCATION);
584             *ptr = state->bufferObject;
585             isClientStateParam = true;
586             break;
587             }
588         case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: {
589             const GLClientState::VertexAttribState *state = getState(GLClientState::POINTSIZE_LOCATION);
590             *ptr = state->bufferObject;
591             isClientStateParam = true;
592             break;
593             }
594         case GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES: {
595             const GLClientState::VertexAttribState *state = getState(GLClientState::MATRIXINDEX_LOCATION);
596             *ptr = state->bufferObject;
597             isClientStateParam = true;
598             break;
599             }
600         case GL_WEIGHT_ARRAY_BUFFER_BINDING_OES: {
601             const GLClientState::VertexAttribState *state = getState(GLClientState::WEIGHT_LOCATION);
602             *ptr = state->bufferObject;
603             isClientStateParam = true;
604             break;
605             }
606         case GL_ARRAY_BUFFER_BINDING: {
607             int buffer = getBuffer(GL_ARRAY_BUFFER);
608             *ptr = buffer;
609             isClientStateParam = true;
610             break;
611             }
612         case GL_ELEMENT_ARRAY_BUFFER_BINDING: {
613             int buffer = getBuffer(GL_ELEMENT_ARRAY_BUFFER);
614             *ptr = buffer;
615             isClientStateParam = true;
616             break;
617             }
618         case GL_MAX_VERTEX_ATTRIBS: {
619             if (m_maxVertexAttribsDirty) {
620                 isClientStateParam = false;
621             } else {
622                 *ptr = m_maxVertexAttribs;
623                 isClientStateParam = true;
624             }
625             break;
626             }
627         }
628         return isClientStateParam;
629     }
630 
631 };
632 #endif
633