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