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 #include "GLClientState.h"
17 #include "ErrorLog.h"
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include "glUtils.h"
22 #include <cutils/log.h>
23 
24 #ifndef MAX
25 #define MAX(a, b) ((a) < (b) ? (b) : (a))
26 #endif
27 
GLClientState(int nLocations)28 GLClientState::GLClientState(int nLocations)
29 {
30     if (nLocations < LAST_LOCATION) {
31         nLocations = LAST_LOCATION;
32     }
33     m_nLocations = nLocations;
34     m_states = new VertexAttribState[m_nLocations];
35     for (int i = 0; i < m_nLocations; i++) {
36         m_states[i].enabled = 0;
37         m_states[i].enableDirty = false;
38         m_states[i].data = 0;
39     }
40     m_currentArrayVbo = 0;
41     m_currentIndexVbo = 0;
42     // init gl constans;
43     m_states[VERTEX_LOCATION].glConst = GL_VERTEX_ARRAY;
44     m_states[NORMAL_LOCATION].glConst = GL_NORMAL_ARRAY;
45     m_states[COLOR_LOCATION].glConst = GL_COLOR_ARRAY;
46     m_states[POINTSIZE_LOCATION].glConst = GL_POINT_SIZE_ARRAY_OES;
47     m_states[TEXCOORD0_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
48     m_states[TEXCOORD1_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
49     m_states[TEXCOORD2_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
50     m_states[TEXCOORD3_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
51     m_states[TEXCOORD4_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
52     m_states[TEXCOORD5_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
53     m_states[TEXCOORD6_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
54     m_states[TEXCOORD7_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY;
55     m_states[MATRIXINDEX_LOCATION].glConst = GL_MATRIX_INDEX_ARRAY_OES;
56     m_states[WEIGHT_LOCATION].glConst = GL_WEIGHT_ARRAY_OES;
57     m_activeTexture = 0;
58     m_currentProgram = 0;
59 
60     m_pixelStore.unpack_alignment = 4;
61     m_pixelStore.pack_alignment = 4;
62 
63     memset(m_tex.unit, 0, sizeof(m_tex.unit));
64     m_tex.activeUnit = &m_tex.unit[0];
65     m_tex.textures = NULL;
66     m_tex.numTextures = 0;
67     m_tex.allocTextures = 0;
68 
69     m_maxVertexAttribsDirty = true;
70 }
71 
~GLClientState()72 GLClientState::~GLClientState()
73 {
74     delete m_states;
75 }
76 
enable(int location,int state)77 void GLClientState::enable(int location, int state)
78 {
79     if (!validLocation(location)) {
80         return;
81     }
82 
83     m_states[location].enableDirty |= (state != m_states[location].enabled);
84     m_states[location].enabled = state;
85 }
86 
setState(int location,int size,GLenum type,GLboolean normalized,GLsizei stride,const void * data)87 void GLClientState::setState(int location, int size, GLenum type, GLboolean normalized, GLsizei stride, const void *data)
88 {
89     if (!validLocation(location)) {
90         return;
91     }
92     m_states[location].size = size;
93     m_states[location].type = type;
94     m_states[location].stride = stride;
95     m_states[location].data = (void*)data;
96     m_states[location].bufferObject = m_currentArrayVbo;
97     m_states[location].elementSize = size ? (glSizeof(type) * size) : 0;
98     m_states[location].normalized = normalized;
99 }
100 
setBufferObject(int location,GLuint id)101 void GLClientState::setBufferObject(int location, GLuint id)
102 {
103     if (!validLocation(location)) {
104         return;
105     }
106 
107     m_states[location].bufferObject = id;
108 }
109 
getState(int location)110 const GLClientState::VertexAttribState * GLClientState::getState(int location)
111 {
112     if (!validLocation(location)) {
113         return NULL;
114     }
115     return & m_states[location];
116 }
117 
getStateAndEnableDirty(int location,bool * enableChanged)118 const GLClientState::VertexAttribState * GLClientState::getStateAndEnableDirty(int location, bool *enableChanged)
119 {
120     if (!validLocation(location)) {
121         return NULL;
122     }
123 
124     if (enableChanged) {
125         *enableChanged = m_states[location].enableDirty;
126     }
127 
128     m_states[location].enableDirty = false;
129     return & m_states[location];
130 }
131 
getLocation(GLenum loc)132 int GLClientState::getLocation(GLenum loc)
133 {
134     int retval;
135 
136     switch(loc) {
137     case GL_VERTEX_ARRAY:
138         retval = int(VERTEX_LOCATION);
139         break;
140     case GL_NORMAL_ARRAY:
141         retval = int(NORMAL_LOCATION);
142         break;
143     case GL_COLOR_ARRAY:
144         retval = int(COLOR_LOCATION);
145         break;
146     case GL_POINT_SIZE_ARRAY_OES:
147         retval = int(POINTSIZE_LOCATION);
148         break;
149     case GL_TEXTURE_COORD_ARRAY:
150         retval = int (TEXCOORD0_LOCATION + m_activeTexture);
151         break;
152     case GL_MATRIX_INDEX_ARRAY_OES:
153         retval = int (MATRIXINDEX_LOCATION);
154         break;
155     case GL_WEIGHT_ARRAY_OES:
156         retval = int (WEIGHT_LOCATION);
157         break;
158     default:
159         retval = loc;
160     }
161     return retval;
162 }
163 
getClientStatePointer(GLenum pname,GLvoid ** params)164 void GLClientState::getClientStatePointer(GLenum pname, GLvoid** params)
165 {
166     const GLClientState::VertexAttribState *state = NULL;
167     switch (pname) {
168     case GL_VERTEX_ARRAY_POINTER: {
169         state = getState(GLClientState::VERTEX_LOCATION);
170         break;
171         }
172     case GL_NORMAL_ARRAY_POINTER: {
173         state = getState(GLClientState::NORMAL_LOCATION);
174         break;
175         }
176     case GL_COLOR_ARRAY_POINTER: {
177         state = getState(GLClientState::COLOR_LOCATION);
178         break;
179         }
180     case GL_TEXTURE_COORD_ARRAY_POINTER: {
181         state = getState(getActiveTexture() + GLClientState::TEXCOORD0_LOCATION);
182         break;
183         }
184     case GL_POINT_SIZE_ARRAY_POINTER_OES: {
185         state = getState(GLClientState::POINTSIZE_LOCATION);
186         break;
187         }
188     case GL_MATRIX_INDEX_ARRAY_POINTER_OES: {
189         state = getState(GLClientState::MATRIXINDEX_LOCATION);
190         break;
191         }
192     case GL_WEIGHT_ARRAY_POINTER_OES: {
193         state = getState(GLClientState::WEIGHT_LOCATION);
194         break;
195         }
196     }
197     if (state && params)
198         *params = state->data;
199 }
200 
setPixelStore(GLenum param,GLint value)201 int GLClientState::setPixelStore(GLenum param, GLint value)
202 {
203     int retval = 0;
204     switch(param) {
205     case GL_UNPACK_ALIGNMENT:
206         if (value == 1 || value == 2 || value == 4 || value == 8) {
207             m_pixelStore.unpack_alignment = value;
208         } else {
209             retval =  GL_INVALID_VALUE;
210         }
211         break;
212     case GL_PACK_ALIGNMENT:
213         if (value == 1 || value == 2 || value == 4 || value == 8) {
214             m_pixelStore.pack_alignment = value;
215         } else {
216             retval =  GL_INVALID_VALUE;
217         }
218         break;
219         default:
220             retval = GL_INVALID_ENUM;
221     }
222     return retval;
223 }
224 
225 
226 
227 
pixelDataSize(GLsizei width,GLsizei height,GLenum format,GLenum type,int pack) const228 size_t GLClientState::pixelDataSize(GLsizei width, GLsizei height, GLenum format, GLenum type, int pack) const
229 {
230     if (width <= 0 || height <= 0) return 0;
231 
232     int pixelsize = glUtilsPixelBitSize(format, type) >> 3;
233 
234     int alignment = pack ? m_pixelStore.pack_alignment : m_pixelStore.unpack_alignment;
235 
236     if (pixelsize == 0 ) {
237         ERR("unknown pixel size: width: %d height: %d format: %d type: %d pack: %d align: %d\n",
238              width, height, format, type, pack, alignment);
239     }
240     size_t linesize = pixelsize * width;
241     size_t aligned_linesize = int(linesize / alignment) * alignment;
242     if (aligned_linesize < linesize) {
243         aligned_linesize += alignment;
244     }
245     return aligned_linesize * height;
246 }
247 
setActiveTextureUnit(GLenum texture)248 GLenum GLClientState::setActiveTextureUnit(GLenum texture)
249 {
250     GLuint unit = texture - GL_TEXTURE0;
251     if (unit >= MAX_TEXTURE_UNITS) {
252         return GL_INVALID_ENUM;
253     }
254     m_tex.activeUnit = &m_tex.unit[unit];
255     return GL_NO_ERROR;
256 }
257 
getActiveTextureUnit() const258 GLenum GLClientState::getActiveTextureUnit() const
259 {
260     return GL_TEXTURE0 + (m_tex.activeUnit - &m_tex.unit[0]);
261 }
262 
enableTextureTarget(GLenum target)263 void GLClientState::enableTextureTarget(GLenum target)
264 {
265     switch (target) {
266     case GL_TEXTURE_2D:
267         m_tex.activeUnit->enables |= (1u << TEXTURE_2D);
268         break;
269     case GL_TEXTURE_EXTERNAL_OES:
270         m_tex.activeUnit->enables |= (1u << TEXTURE_EXTERNAL);
271         break;
272     }
273 }
274 
disableTextureTarget(GLenum target)275 void GLClientState::disableTextureTarget(GLenum target)
276 {
277     switch (target) {
278     case GL_TEXTURE_2D:
279         m_tex.activeUnit->enables &= ~(1u << TEXTURE_2D);
280         break;
281     case GL_TEXTURE_EXTERNAL_OES:
282         m_tex.activeUnit->enables &= ~(1u << TEXTURE_EXTERNAL);
283         break;
284     }
285 }
286 
getPriorityEnabledTarget(GLenum allDisabled) const287 GLenum GLClientState::getPriorityEnabledTarget(GLenum allDisabled) const
288 {
289     unsigned int enables = m_tex.activeUnit->enables;
290     if (enables & (1u << TEXTURE_EXTERNAL)) {
291         return GL_TEXTURE_EXTERNAL_OES;
292     } else if (enables & (1u << TEXTURE_2D)) {
293         return GL_TEXTURE_2D;
294     } else {
295         return allDisabled;
296     }
297 }
298 
compareTexId(const void * pid,const void * prec)299 int GLClientState::compareTexId(const void* pid, const void* prec)
300 {
301     const GLuint* id = (const GLuint*)pid;
302     const TextureRec* rec = (const TextureRec*)prec;
303     return (GLint)(*id) - (GLint)rec->id;
304 }
305 
bindTexture(GLenum target,GLuint texture,GLboolean * firstUse)306 GLenum GLClientState::bindTexture(GLenum target, GLuint texture,
307         GLboolean* firstUse)
308 {
309     GLboolean first = GL_FALSE;
310     TextureRec* texrec = NULL;
311     if (texture != 0) {
312         if (m_tex.textures) {
313             texrec = (TextureRec*)bsearch(&texture, m_tex.textures,
314                     m_tex.numTextures, sizeof(TextureRec), compareTexId);
315         }
316         if (!texrec) {
317             if (!(texrec = addTextureRec(texture, target))) {
318                 return GL_OUT_OF_MEMORY;
319             }
320             first = GL_TRUE;
321         }
322         if (target != texrec->target) {
323             return GL_INVALID_OPERATION;
324         }
325     }
326 
327     switch (target) {
328     case GL_TEXTURE_2D:
329         m_tex.activeUnit->texture[TEXTURE_2D] = texture;
330         break;
331     case GL_TEXTURE_EXTERNAL_OES:
332         m_tex.activeUnit->texture[TEXTURE_EXTERNAL] = texture;
333         break;
334     }
335 
336     if (firstUse) {
337         *firstUse = first;
338     }
339 
340     return GL_NO_ERROR;
341 }
342 
addTextureRec(GLuint id,GLenum target)343 GLClientState::TextureRec* GLClientState::addTextureRec(GLuint id,
344         GLenum target)
345 {
346     if (m_tex.numTextures == m_tex.allocTextures) {
347         const GLuint MAX_TEXTURES = 0xFFFFFFFFu;
348 
349         GLuint newAlloc;
350         if (MAX_TEXTURES - m_tex.allocTextures >= m_tex.allocTextures) {
351             newAlloc = MAX(4, 2 * m_tex.allocTextures);
352         } else {
353             if (m_tex.allocTextures == MAX_TEXTURES) {
354                 return NULL;
355             }
356             newAlloc = MAX_TEXTURES;
357         }
358 
359         TextureRec* newTextures = (TextureRec*)realloc(m_tex.textures,
360                 newAlloc * sizeof(TextureRec));
361         if (!newTextures) {
362             return NULL;
363         }
364 
365         m_tex.textures = newTextures;
366         m_tex.allocTextures = newAlloc;
367     }
368 
369     TextureRec* tex = m_tex.textures + m_tex.numTextures;
370     TextureRec* prev = tex - 1;
371     while (tex != m_tex.textures && id < prev->id) {
372         *tex-- = *prev--;
373     }
374     tex->id = id;
375     tex->target = target;
376     m_tex.numTextures++;
377 
378     return tex;
379 }
380 
getBoundTexture(GLenum target) const381 GLuint GLClientState::getBoundTexture(GLenum target) const
382 {
383     switch (target) {
384     case GL_TEXTURE_2D:
385         return m_tex.activeUnit->texture[TEXTURE_2D];
386     case GL_TEXTURE_EXTERNAL_OES:
387         return m_tex.activeUnit->texture[TEXTURE_EXTERNAL];
388     default:
389         return 0;
390     }
391 }
392 
deleteTextures(GLsizei n,const GLuint * textures)393 void GLClientState::deleteTextures(GLsizei n, const GLuint* textures)
394 {
395     // Updating the textures array could be made more efficient when deleting
396     // several textures:
397     // - compacting the array could be done in a single pass once the deleted
398     //   textures are marked, or
399     // - could swap deleted textures to the end and re-sort.
400     TextureRec* texrec;
401     for (const GLuint* texture = textures; texture != textures + n; texture++) {
402         texrec = (TextureRec*)bsearch(texture, m_tex.textures,
403                 m_tex.numTextures, sizeof(TextureRec), compareTexId);
404         if (texrec) {
405             const TextureRec* end = m_tex.textures + m_tex.numTextures;
406             memmove(texrec, texrec + 1,
407                     (end - texrec - 1) * sizeof(TextureRec));
408             m_tex.numTextures--;
409 
410             for (TextureUnit* unit = m_tex.unit;
411                  unit != m_tex.unit + MAX_TEXTURE_UNITS;
412                  unit++)
413             {
414                 if (unit->texture[TEXTURE_2D] == *texture) {
415                     unit->texture[TEXTURE_2D] = 0;
416                 } else if (unit->texture[TEXTURE_EXTERNAL] == *texture) {
417                     unit->texture[TEXTURE_EXTERNAL] = 0;
418                 }
419             }
420         }
421     }
422 }
423