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