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