GLClientState.cpp revision db8565739a0c41c0685928caace975a766ffc9ca
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
28GLClientState::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
70GLClientState::~GLClientState()
71{
72    delete m_states;
73}
74
75void 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
85void 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 = size ? (glSizeof(type) * size) : 0;
96    m_states[location].normalized = normalized;
97}
98
99void GLClientState::setBufferObject(int location, GLuint id)
100{
101    if (!validLocation(location)) {
102        return;
103    }
104
105    m_states[location].bufferObject = id;
106}
107
108const GLClientState::VertexAttribState * GLClientState::getState(int location)
109{
110    if (!validLocation(location)) {
111        return NULL;
112    }
113    return & m_states[location];
114}
115
116const 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
130int 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
162void 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
199int 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
226size_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
246GLenum GLClientState::setActiveTextureUnit(GLenum texture)
247{
248    GLuint unit = texture - GL_TEXTURE0;
249    if (unit >= MAX_TEXTURE_UNITS) {
250        return GL_INVALID_ENUM;
251    }
252    m_tex.activeUnit = &m_tex.unit[unit];
253    return GL_NO_ERROR;
254}
255
256GLenum GLClientState::getActiveTextureUnit() const
257{
258    return GL_TEXTURE0 + (m_tex.activeUnit - &m_tex.unit[0]);
259}
260
261void 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
273void 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
285GLenum 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
297int 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
304GLenum 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
341GLClientState::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
379GLuint 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
391void 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