array.cpp revision 92dc3fc52cf097bd105460cf377779bdcf146d62
1/*
2** Copyright 2006, 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
17#include <stdlib.h>
18#include <stdio.h>
19
20#include "context.h"
21#include "fp.h"
22#include "state.h"
23#include "matrix.h"
24#include "vertex.h"
25#include "light.h"
26#include "primitives.h"
27#include "texture.h"
28#include "BufferObjectManager.h"
29
30// ----------------------------------------------------------------------------
31
32#define VC_CACHE_STATISTICS     0
33#define VC_CACHE_TYPE_NONE      0
34#define VC_CACHE_TYPE_INDEXED   1
35#define VC_CACHE_TYPE_LRU       2
36#define VC_CACHE_TYPE           VC_CACHE_TYPE_INDEXED
37
38#if VC_CACHE_STATISTICS
39#include <utils/Timers.h>
40#endif
41
42// ----------------------------------------------------------------------------
43
44namespace android {
45
46static void validate_arrays(ogles_context_t* c, GLenum mode);
47
48static void compileElements__generic(ogles_context_t*,
49        vertex_t*, GLint, GLsizei);
50static void compileElement__generic(ogles_context_t*,
51        vertex_t*, GLint);
52
53static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei);
54static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei);
55static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei);
56static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei);
57static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei);
58static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei);
59static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei);
60
61static void drawIndexedPrimitivesPoints(ogles_context_t*,
62        GLsizei, const GLvoid*);
63static void drawIndexedPrimitivesLineStrip(ogles_context_t*,
64        GLsizei, const GLvoid*);
65static void drawIndexedPrimitivesLineLoop(ogles_context_t*,
66        GLsizei, const GLvoid*);
67static void drawIndexedPrimitivesLines(ogles_context_t*,
68        GLsizei, const GLvoid*);
69static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*,
70        GLsizei, const GLvoid*);
71static void drawIndexedPrimitivesTriangleFan(ogles_context_t*,
72        GLsizei, const GLvoid*);
73static void drawIndexedPrimitivesTriangles(ogles_context_t*,
74        GLsizei, const GLvoid*);
75
76// ----------------------------------------------------------------------------
77
78typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei);
79static const arrays_prims_fct_t drawArraysPrims[] = {
80    drawPrimitivesPoints,
81    drawPrimitivesLines,
82    drawPrimitivesLineLoop,
83    drawPrimitivesLineStrip,
84    drawPrimitivesTriangles,
85    drawPrimitivesTriangleStrip,
86    drawPrimitivesTriangleFan
87};
88
89typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*);
90static const elements_prims_fct_t drawElementsPrims[] = {
91    drawIndexedPrimitivesPoints,
92    drawIndexedPrimitivesLines,
93    drawIndexedPrimitivesLineLoop,
94    drawIndexedPrimitivesLineStrip,
95    drawIndexedPrimitivesTriangles,
96    drawIndexedPrimitivesTriangleStrip,
97    drawIndexedPrimitivesTriangleFan
98};
99
100// ----------------------------------------------------------------------------
101#if 0
102#pragma mark -
103#endif
104
105void ogles_init_array(ogles_context_t* c)
106{
107    c->arrays.vertex.size = 4;
108    c->arrays.vertex.type = GL_FLOAT;
109    c->arrays.color.size = 4;
110    c->arrays.color.type = GL_FLOAT;
111    c->arrays.normal.size = 4;
112    c->arrays.normal.type = GL_FLOAT;
113    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
114        c->arrays.texture[i].size = 4;
115        c->arrays.texture[i].type = GL_FLOAT;
116    }
117    c->vc.init();
118
119    if (!c->vc.vBuffer) {
120        // this could have failed
121        ogles_error(c, GL_OUT_OF_MEMORY);
122    }
123}
124
125void ogles_uninit_array(ogles_context_t* c)
126{
127    c->vc.uninit();
128}
129
130// ----------------------------------------------------------------------------
131#if 0
132#pragma mark -
133#pragma mark Array fetchers
134#endif
135
136static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) {
137    memcpy(v, c->current.color.v, sizeof(vec4_t));
138}
139static void currentColor_clamp(ogles_context_t* c, GLfixed* v, const GLvoid*) {
140    memcpy(v, c->currentColorClamped.v, sizeof(vec4_t));
141}
142static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) {
143    memcpy(v, c->currentNormal.v, sizeof(vec3_t));
144}
145static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) {
146    memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t));
147}
148
149
150static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) {
151}
152static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
153    v[0] = gglIntToFixed(p[0]);
154    v[1] = gglIntToFixed(p[1]);
155}
156static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) {
157    v[0] = gglIntToFixed(p[0]);
158    v[1] = gglIntToFixed(p[1]);
159}
160static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
161    memcpy(v, p, 2*sizeof(GLfixed));
162}
163static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
164    v[0] = gglFloatToFixed(p[0]);
165    v[1] = gglFloatToFixed(p[1]);
166}
167static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
168    v[0] = gglIntToFixed(p[0]);
169    v[1] = gglIntToFixed(p[1]);
170    v[2] = gglIntToFixed(p[2]);
171}
172static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
173    v[0] = gglIntToFixed(p[0]);
174    v[1] = gglIntToFixed(p[1]);
175    v[2] = gglIntToFixed(p[2]);
176}
177static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
178    memcpy(v, p, 3*sizeof(GLfixed));
179}
180static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
181    v[0] = gglFloatToFixed(p[0]);
182    v[1] = gglFloatToFixed(p[1]);
183    v[2] = gglFloatToFixed(p[2]);
184}
185static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
186    v[0] = gglIntToFixed(p[0]);
187    v[1] = gglIntToFixed(p[1]);
188    v[2] = gglIntToFixed(p[2]);
189    v[3] = gglIntToFixed(p[3]);
190}
191static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) {
192    v[0] = gglIntToFixed(p[0]);
193    v[1] = gglIntToFixed(p[1]);
194    v[2] = gglIntToFixed(p[2]);
195    v[3] = gglIntToFixed(p[3]);
196}
197static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
198    memcpy(v, p, 4*sizeof(GLfixed));
199}
200static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
201    v[0] = gglFloatToFixed(p[0]);
202    v[1] = gglFloatToFixed(p[1]);
203    v[2] = gglFloatToFixed(p[2]);
204    v[3] = gglFloatToFixed(p[3]);
205}
206static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) {
207    v[0] = GGL_UB_TO_X(p[0]);
208    v[1] = GGL_UB_TO_X(p[1]);
209    v[2] = GGL_UB_TO_X(p[2]);
210    v[3] = GGL_UB_TO_X(p[3]);
211}
212static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
213    v[0] = gglClampx(p[0]);
214    v[1] = gglClampx(p[1]);
215    v[2] = gglClampx(p[2]);
216    v[3] = gglClampx(p[3]);
217}
218static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
219    v[0] = gglClampx(gglFloatToFixed(p[0]));
220    v[1] = gglClampx(gglFloatToFixed(p[1]));
221    v[2] = gglClampx(gglFloatToFixed(p[2]));
222    v[3] = gglClampx(gglFloatToFixed(p[3]));
223}
224static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) {
225    v[0] = GGL_UB_TO_X(p[0]);
226    v[1] = GGL_UB_TO_X(p[1]);
227    v[2] = GGL_UB_TO_X(p[2]);
228    v[3] = 0x10000;
229}
230static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
231    v[0] = gglClampx(p[0]);
232    v[1] = gglClampx(p[1]);
233    v[2] = gglClampx(p[2]);
234    v[3] = 0x10000;
235}
236static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
237    v[0] = gglClampx(gglFloatToFixed(p[0]));
238    v[1] = gglClampx(gglFloatToFixed(p[1]));
239    v[2] = gglClampx(gglFloatToFixed(p[2]));
240    v[3] = 0x10000;
241}
242static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
243    v[0] = GGL_B_TO_X(p[0]);
244    v[1] = GGL_B_TO_X(p[1]);
245    v[2] = GGL_B_TO_X(p[2]);
246}
247static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
248    v[0] = GGL_S_TO_X(p[0]);
249    v[1] = GGL_S_TO_X(p[1]);
250    v[2] = GGL_S_TO_X(p[2]);
251}
252
253typedef array_t::fetcher_t fn_t;
254
255static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x}
256    { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
257         (fn_t)fetch3f, 0, 0, 0, 0, 0,
258         (fn_t)fetch3x },
259    { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0,
260         (fn_t)fetch4f, 0, 0, 0, 0, 0,
261         (fn_t)fetch4x },
262};
263static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x}
264    { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
265         (fn_t)fetchClamp3f, 0, 0, 0, 0, 0,
266         (fn_t)fetchClamp3x },
267    { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0,
268         (fn_t)fetchClamp4f, 0, 0, 0, 0, 0,
269         (fn_t)fetchClamp4x },
270};
271static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x}
272    { (fn_t)fetchExpand3b, 0,
273      (fn_t)fetchExpand3s, 0, 0, 0,
274      (fn_t)fetch3f, 0, 0, 0, 0, 0,
275      (fn_t)fetch3x },
276};
277static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x}
278    { (fn_t)fetch2b, 0,
279      (fn_t)fetch2s, 0, 0, 0,
280      (fn_t)fetch2f, 0, 0, 0, 0, 0,
281      (fn_t)fetch3x },
282    { (fn_t)fetch3b, 0,
283      (fn_t)fetch3s, 0, 0, 0,
284      (fn_t)fetch3f, 0, 0, 0, 0, 0,
285      (fn_t)fetch3x },
286    { (fn_t)fetch4b, 0,
287      (fn_t)fetch4s, 0, 0, 0,
288      (fn_t)fetch4f, 0, 0, 0, 0, 0,
289      (fn_t)fetch4x }
290};
291static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x}
292    { (fn_t)fetch2b, 0,
293      (fn_t)fetch2s, 0, 0, 0,
294      (fn_t)fetch2f, 0, 0, 0, 0, 0,
295      (fn_t)fetch2x },
296    { (fn_t)fetch3b, 0,
297      (fn_t)fetch3s, 0, 0, 0,
298      (fn_t)fetch3f, 0, 0, 0, 0, 0,
299      (fn_t)fetch3x },
300    { (fn_t)fetch4b, 0,
301      (fn_t)fetch4s, 0, 0, 0,
302      (fn_t)fetch4f, 0, 0, 0, 0, 0,
303      (fn_t)fetch4x }
304};
305
306// ----------------------------------------------------------------------------
307#if 0
308#pragma mark -
309#pragma mark array_t
310#endif
311
312void array_t::init(
313        GLint size, GLenum type, GLsizei stride,
314        const GLvoid *pointer, const buffer_t* bo, GLsizei count)
315{
316    if (!stride) {
317        stride = size;
318        switch (type) {
319        case GL_SHORT:
320        case GL_UNSIGNED_SHORT:
321            stride *= 2;
322            break;
323        case GL_FLOAT:
324        case GL_FIXED:
325            stride *= 4;
326            break;
327        }
328    }
329    this->size = size;
330    this->type = type;
331    this->stride = stride;
332    this->pointer = pointer;
333    this->bo = bo;
334    this->bounds = count;
335}
336
337inline void array_t::resolve()
338{
339    physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer;
340}
341
342// ----------------------------------------------------------------------------
343#if 0
344#pragma mark -
345#pragma mark vertex_cache_t
346#endif
347
348void vertex_cache_t::init()
349{
350    // make sure the size of vertex_t allows cache-line alignment
351    CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize;
352
353    const int align = 32;
354    const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
355    const size_t size = s*sizeof(vertex_t) + align;
356    base = malloc(size);
357    if (base) {
358        memset(base, 0, size);
359        vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1));
360        vCache = vBuffer + VERTEX_BUFFER_SIZE;
361        sequence = 0;
362    }
363}
364
365void vertex_cache_t::uninit()
366{
367    free(base);
368    base = vBuffer = vCache = 0;
369}
370
371void vertex_cache_t::clear()
372{
373#if VC_CACHE_STATISTICS
374    startTime = systemTime(SYSTEM_TIME_THREAD);
375    total = 0;
376    misses = 0;
377#endif
378
379#if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
380    vertex_t* v = vBuffer;
381    size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
382    do {
383        v->mru = 0;
384        v++;
385    } while (--count);
386#endif
387
388    sequence += INDEX_SEQ;
389    if (sequence >= 0x80000000LU) {
390        sequence = INDEX_SEQ;
391        vertex_t* v = vBuffer;
392        size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
393        do {
394            v->index = 0;
395            v++;
396        } while (--count);
397    }
398}
399
400#if VC_CACHE_STATISTICS
401void vertex_cache_t::dump_stats(GLenum mode)
402{
403    nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime;
404    uint32_t hits = total - misses;
405    uint32_t prim_count;
406    switch (mode) {
407    case GL_POINTS:             prim_count = total;         break;
408    case GL_LINE_STRIP:         prim_count = total - 1;     break;
409    case GL_LINE_LOOP:          prim_count = total - 1;     break;
410    case GL_LINES:              prim_count = total / 2;     break;
411    case GL_TRIANGLE_STRIP:     prim_count = total - 2;     break;
412    case GL_TRIANGLE_FAN:       prim_count = total - 2;     break;
413    case GL_TRIANGLES:          prim_count = total / 3;     break;
414    default:    return;
415    }
416    printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%,"
417            " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n",
418            total, hits, misses, (hits*100)/total,
419            prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time),
420            float(misses) / prim_count);
421}
422#else
423void vertex_cache_t::dump_stats(GLenum /*mode*/)
424{
425}
426#endif
427
428// ----------------------------------------------------------------------------
429#if 0
430#pragma mark -
431#endif
432
433static __attribute__((noinline))
434void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable)
435{
436    const int tmu = c->arrays.activeTexture;
437    array_t* a;
438    switch (array) {
439    case GL_COLOR_ARRAY:            a = &c->arrays.color;           break;
440    case GL_NORMAL_ARRAY:           a = &c->arrays.normal;          break;
441    case GL_TEXTURE_COORD_ARRAY:    a = &c->arrays.texture[tmu];    break;
442    case GL_VERTEX_ARRAY:           a = &c->arrays.vertex;          break;
443    default:
444        ogles_error(c, GL_INVALID_ENUM);
445        return;
446    }
447    a->enable = enable ? GL_TRUE : GL_FALSE;
448}
449
450// ----------------------------------------------------------------------------
451#if 0
452#pragma mark -
453#pragma mark Vertex Cache
454#endif
455
456static __attribute__((noinline))
457vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index)
458{
459    #if VC_CACHE_STATISTICS
460        c->vc.misses++;
461    #endif
462    if (ggl_unlikely(v->locked)) {
463        // we're just looking for an entry in the cache that is not locked.
464        // and we know that there cannot be more than 2 locked entries
465        // because a triangle needs at most 3 vertices.
466        // We never use the first and second entries because they might be in
467        // use by the striper or faner. Any other entry will do as long as
468        // it's not locked.
469        // We compute directly the index of a "free" entry from the locked
470        // state of v[2] and v[3].
471        v = c->vc.vBuffer + 2;
472        v += v[0].locked | (v[1].locked<<1);
473    }
474    // note: compileElement clears v->flags
475    c->arrays.compileElement(c, v, index);
476    v->locked = 1;
477    return v;
478}
479
480static __attribute__((noinline))
481vertex_t* fetch_vertex(ogles_context_t* c, size_t index)
482{
483    index |= c->vc.sequence;
484
485#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED
486
487    vertex_t* const v = c->vc.vCache +
488            (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1));
489
490    if (ggl_likely(v->index == index)) {
491        v->locked = 1;
492        return v;
493    }
494    return cache_vertex(c, v, index);
495
496#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
497
498    vertex_t* v = c->vc.vCache +
499            (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2;
500
501    // always record LRU in v[0]
502    if (ggl_likely(v[0].index == index)) {
503        v[0].locked = 1;
504        v[0].mru = 0;
505        return &v[0];
506    }
507
508    if (ggl_likely(v[1].index == index)) {
509        v[1].locked = 1;
510        v[0].mru = 1;
511        return &v[1];
512    }
513
514    const int lru = 1 - v[0].mru;
515    v[0].mru = lru;
516    return cache_vertex(c, &v[lru], index);
517
518#elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE
519
520    // just for debugging...
521    vertex_t* v = c->vc.vBuffer + 2;
522    return cache_vertex(c, v, index);
523
524#endif
525}
526
527// ----------------------------------------------------------------------------
528#if 0
529#pragma mark -
530#pragma mark Primitive Assembly
531#endif
532
533void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count)
534{
535    if (ggl_unlikely(count < 1))
536        return;
537
538    // vertex cache size must be multiple of 1
539    const GLsizei vcs =
540            (vertex_cache_t::VERTEX_BUFFER_SIZE +
541             vertex_cache_t::VERTEX_CACHE_SIZE);
542    do {
543        vertex_t* v = c->vc.vBuffer;
544        GLsizei num = count > vcs ? vcs : count;
545        c->arrays.cull = vertex_t::CLIP_ALL;
546        c->arrays.compileElements(c, v, first, num);
547        first += num;
548        count -= num;
549        if (!c->arrays.cull) {
550            // quick/trivial reject of the whole batch
551            do {
552                const uint32_t cc = v[0].flags;
553                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
554                    c->prims.renderPoint(c, v);
555                v++;
556                num--;
557            } while (num);
558        }
559    } while (count);
560}
561
562// ----------------------------------------------------------------------------
563
564void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count)
565{
566    if (ggl_unlikely(count < 2))
567        return;
568
569    vertex_t *v, *v0, *v1;
570    c->arrays.cull = vertex_t::CLIP_ALL;
571    c->arrays.compileElement(c, c->vc.vBuffer, first);
572    first += 1;
573    count -= 1;
574
575    // vertex cache size must be multiple of 1
576    const GLsizei vcs =
577        (vertex_cache_t::VERTEX_BUFFER_SIZE +
578         vertex_cache_t::VERTEX_CACHE_SIZE - 1);
579    do {
580        v0 = c->vc.vBuffer + 0;
581        v  = c->vc.vBuffer + 1;
582        GLsizei num = count > vcs ? vcs : count;
583        c->arrays.compileElements(c, v, first, num);
584        first += num;
585        count -= num;
586        if (!c->arrays.cull) {
587            // quick/trivial reject of the whole batch
588            do {
589                v1 = v++;
590                const uint32_t cc = v0->flags & v1->flags;
591                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
592                    c->prims.renderLine(c, v0, v1);
593                v0 = v1;
594                num--;
595            } while (num);
596        }
597        // copy back the last processed vertex
598        c->vc.vBuffer[0] = *v0;
599        c->arrays.cull = v0->flags & vertex_t::CLIP_ALL;
600    } while (count);
601}
602
603void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count)
604{
605    if (ggl_unlikely(count < 2))
606        return;
607    drawPrimitivesLineStrip(c, first, count);
608    if (ggl_likely(count >= 3)) {
609        vertex_t* v0 = c->vc.vBuffer;
610        vertex_t* v1 = c->vc.vBuffer + 1;
611        c->arrays.compileElement(c, v1, first);
612        const uint32_t cc = v0->flags & v1->flags;
613        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
614            c->prims.renderLine(c, v0, v1);
615    }
616}
617
618void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count)
619{
620    if (ggl_unlikely(count < 2))
621        return;
622
623    // vertex cache size must be multiple of 2
624    const GLsizei vcs =
625        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
626        vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2;
627    do {
628        vertex_t* v = c->vc.vBuffer;
629        GLsizei num = count > vcs ? vcs : count;
630        c->arrays.cull = vertex_t::CLIP_ALL;
631        c->arrays.compileElements(c, v, first, num);
632        first += num;
633        count -= num;
634        if (!c->arrays.cull) {
635            // quick/trivial reject of the whole batch
636            num -= 2;
637            do {
638                const uint32_t cc = v[0].flags & v[1].flags;
639                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
640                    c->prims.renderLine(c, v, v+1);
641                v += 2;
642                num -= 2;
643            } while (num >= 0);
644        }
645    } while (count >= 2);
646}
647
648// ----------------------------------------------------------------------------
649
650static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c,
651        GLint first, GLsizei count, int winding)
652{
653    // winding == 2 : fan
654    // winding == 1 : strip
655
656    if (ggl_unlikely(count < 3))
657        return;
658
659    vertex_t *v, *v0, *v1, *v2;
660    c->arrays.cull = vertex_t::CLIP_ALL;
661    c->arrays.compileElements(c, c->vc.vBuffer, first, 2);
662    first += 2;
663    count -= 2;
664
665    // vertex cache size must be multiple of 2. This is extremely important
666    // because it allows us to preserve the same winding when the whole
667    // batch is culled. We also need 2 extra vertices in the array, because
668    // we always keep the two first ones.
669    const GLsizei vcs =
670        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
671          vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2;
672    do {
673        v0 = c->vc.vBuffer + 0;
674        v1 = c->vc.vBuffer + 1;
675        v  = c->vc.vBuffer + 2;
676        GLsizei num = count > vcs ? vcs : count;
677        c->arrays.compileElements(c, v, first, num);
678        first += num;
679        count -= num;
680        if (!c->arrays.cull) {
681            // quick/trivial reject of the whole batch
682            do {
683                v2 = v++;
684                const uint32_t cc = v0->flags & v1->flags & v2->flags;
685                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
686                    c->prims.renderTriangle(c, v0, v1, v2);
687                swap(((winding^=1) ? v1 : v0), v2);
688                num--;
689            } while (num);
690        }
691        if (count) {
692            v0 = c->vc.vBuffer + 2 + vcs - 2;
693            v1 = c->vc.vBuffer + 2 + vcs - 1;
694            if ((winding&2) == 0) {
695                // for strips copy back the two last compiled vertices
696                c->vc.vBuffer[0] = *v0;
697            }
698            c->vc.vBuffer[1] = *v1;
699            c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL;
700        }
701    } while (count > 0);
702}
703
704void drawPrimitivesTriangleStrip(ogles_context_t* c,
705        GLint first, GLsizei count) {
706    drawPrimitivesTriangleFanOrStrip(c, first, count, 1);
707}
708
709void drawPrimitivesTriangleFan(ogles_context_t* c,
710        GLint first, GLsizei count) {
711    drawPrimitivesTriangleFanOrStrip(c, first, count, 2);
712}
713
714void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count)
715{
716    if (ggl_unlikely(count < 3))
717        return;
718
719    // vertex cache size must be multiple of 3
720    const GLsizei vcs =
721        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
722        vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3;
723    do {
724        vertex_t* v = c->vc.vBuffer;
725        GLsizei num = count > vcs ? vcs : count;
726        c->arrays.cull = vertex_t::CLIP_ALL;
727        c->arrays.compileElements(c, v, first, num);
728        first += num;
729        count -= num;
730        if (!c->arrays.cull) {
731            // quick/trivial reject of the whole batch
732            num -= 3;
733            do {
734                const uint32_t cc = v[0].flags & v[1].flags & v[2].flags;
735                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
736                    c->prims.renderTriangle(c, v, v+1, v+2);
737                v += 3;
738                num -= 3;
739            } while (num >= 0);
740        }
741    } while (count >= 3);
742}
743
744// ----------------------------------------------------------------------------
745#if 0
746#pragma mark -
747#endif
748
749// this looks goofy, but gcc does a great job with this...
750static inline unsigned int read_index(int type, const GLvoid*& p) {
751    unsigned int r;
752    if (type) {
753        r = *(const GLubyte*)p;
754        p = (const GLubyte*)p + 1;
755    } else {
756        r = *(const GLushort*)p;
757        p = (const GLushort*)p + 1;
758    }
759    return r;
760}
761
762// ----------------------------------------------------------------------------
763
764void drawIndexedPrimitivesPoints(ogles_context_t* c,
765        GLsizei count, const GLvoid *indices)
766{
767    if (ggl_unlikely(count < 1))
768        return;
769    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
770    do {
771        vertex_t * v = fetch_vertex(c, read_index(type, indices));
772        if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL)))
773            c->prims.renderPoint(c, v);
774        v->locked = 0;
775        count--;
776    } while(count);
777}
778
779// ----------------------------------------------------------------------------
780
781void drawIndexedPrimitivesLineStrip(ogles_context_t* c,
782        GLsizei count, const GLvoid *indices)
783{
784    if (ggl_unlikely(count < 2))
785        return;
786
787    vertex_t * const v = c->vc.vBuffer;
788    vertex_t* v0 = v;
789    vertex_t* v1;
790
791    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
792    c->arrays.compileElement(c, v0, read_index(type, indices));
793    count -= 1;
794    do {
795        v1 = fetch_vertex(c, read_index(type, indices));
796        const uint32_t cc = v0->flags & v1->flags;
797        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
798            c->prims.renderLine(c, v0, v1);
799        v0->locked = 0;
800        v0 = v1;
801        count--;
802    } while (count);
803    v1->locked = 0;
804}
805
806void drawIndexedPrimitivesLineLoop(ogles_context_t* c,
807        GLsizei count, const GLvoid *indices)
808{
809    if (ggl_unlikely(count <= 2)) {
810        drawIndexedPrimitivesLines(c, count, indices);
811        return;
812    }
813
814    vertex_t * const v = c->vc.vBuffer;
815    vertex_t* v0 = v;
816    vertex_t* v1;
817
818    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
819    c->arrays.compileElement(c, v0, read_index(type, indices));
820    count -= 1;
821    do {
822        v1 = fetch_vertex(c, read_index(type, indices));
823        const uint32_t cc = v0->flags & v1->flags;
824        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
825            c->prims.renderLine(c, v0, v1);
826        v0->locked = 0;
827        v0 = v1;
828        count--;
829    } while (count);
830    v1->locked = 0;
831
832    v1 = c->vc.vBuffer;
833    const uint32_t cc = v0->flags & v1->flags;
834    if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
835        c->prims.renderLine(c, v0, v1);
836}
837
838void drawIndexedPrimitivesLines(ogles_context_t* c,
839        GLsizei count, const GLvoid *indices)
840{
841    if (ggl_unlikely(count < 2))
842        return;
843
844    count -= 2;
845    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
846    do {
847        vertex_t* const v0 = fetch_vertex(c, read_index(type, indices));
848        vertex_t* const v1 = fetch_vertex(c, read_index(type, indices));
849        const uint32_t cc = v0->flags & v1->flags;
850        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
851            c->prims.renderLine(c, v0, v1);
852        v0->locked = 0;
853        v1->locked = 0;
854        count -= 2;
855    } while (count >= 0);
856}
857
858// ----------------------------------------------------------------------------
859
860static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c,
861        GLsizei count, const GLvoid *indices, int winding)
862{
863    // winding == 2 : fan
864    // winding == 1 : strip
865
866    if (ggl_unlikely(count < 3))
867        return;
868
869    vertex_t * const v = c->vc.vBuffer;
870    vertex_t* v0 = v;
871    vertex_t* v1 = v+1;
872    vertex_t* v2;
873
874    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
875    c->arrays.compileElement(c, v0, read_index(type, indices));
876    c->arrays.compileElement(c, v1, read_index(type, indices));
877    count -= 2;
878
879    // note: GCC 4.1.1 here makes a prety interesting optimization
880    // where it duplicates the loop below based on c->arrays.indicesType
881
882    do {
883        v2 = fetch_vertex(c, read_index(type, indices));
884        const uint32_t cc = v0->flags & v1->flags & v2->flags;
885        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
886            c->prims.renderTriangle(c, v0, v1, v2);
887        vertex_t* & consumed = ((winding^=1) ? v1 : v0);
888        consumed->locked = 0;
889        consumed = v2;
890        count--;
891    } while (count);
892    v0->locked = v1->locked = 0;
893    v2->locked = 0;
894}
895
896void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c,
897        GLsizei count, const GLvoid *indices) {
898    drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1);
899}
900
901void drawIndexedPrimitivesTriangleFan(ogles_context_t* c,
902        GLsizei count, const GLvoid *indices) {
903    drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2);
904}
905
906void drawIndexedPrimitivesTriangles(ogles_context_t* c,
907        GLsizei count, const GLvoid *indices)
908{
909    if (ggl_unlikely(count < 3))
910        return;
911
912    count -= 3;
913    if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) {
914        // This case is probably our most common case...
915        uint16_t const * p = (uint16_t const *)indices;
916        do {
917            vertex_t* const v0 = fetch_vertex(c, *p++);
918            vertex_t* const v1 = fetch_vertex(c, *p++);
919            vertex_t* const v2 = fetch_vertex(c, *p++);
920            const uint32_t cc = v0->flags & v1->flags & v2->flags;
921            if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
922                c->prims.renderTriangle(c, v0, v1, v2);
923            v0->locked = 0;
924            v1->locked = 0;
925            v2->locked = 0;
926            count -= 3;
927        } while (count >= 0);
928    } else {
929        uint8_t const * p = (uint8_t const *)indices;
930        do {
931            vertex_t* const v0 = fetch_vertex(c, *p++);
932            vertex_t* const v1 = fetch_vertex(c, *p++);
933            vertex_t* const v2 = fetch_vertex(c, *p++);
934            const uint32_t cc = v0->flags & v1->flags & v2->flags;
935            if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
936                c->prims.renderTriangle(c, v0, v1, v2);
937            v0->locked = 0;
938            v1->locked = 0;
939            v2->locked = 0;
940            count -= 3;
941        } while (count >= 0);
942    }
943}
944
945// ----------------------------------------------------------------------------
946#if 0
947#pragma mark -
948#pragma mark Array compilers
949#endif
950
951void compileElement__generic(ogles_context_t* c,
952        vertex_t* v, GLint first)
953{
954    v->flags = 0;
955    v->index = first;
956    first &= vertex_cache_t::INDEX_MASK;
957    const GLubyte* vp = c->arrays.vertex.element(first);
958    v->obj.z = 0;
959    v->obj.w = 0x10000;
960    c->arrays.vertex.fetch(c, v->obj.v, vp);
961    c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
962    c->arrays.perspective(c, v);
963}
964
965void compileElements__generic(ogles_context_t* c,
966        vertex_t* v, GLint first, GLsizei count)
967{
968    const GLubyte* vp = c->arrays.vertex.element(
969            first & vertex_cache_t::INDEX_MASK);
970    const size_t stride = c->arrays.vertex.stride;
971    transform_t const* const mvp = &c->transforms.mvp;
972    do {
973        v->flags = 0;
974        v->index = first++;
975        v->obj.z = 0;
976        v->obj.w = 0x10000;
977        c->arrays.vertex.fetch(c, v->obj.v, vp);
978        c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
979        c->arrays.perspective(c, v);
980        vp += stride;
981        v++;
982    } while (--count);
983}
984
985/*
986void compileElements__3x_full(ogles_context_t* c,
987        vertex_t* v, GLint first, GLsizei count)
988{
989    const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first);
990    const size_t stride = c->arrays.vertex.stride / 4;
991//    const GLfixed* const& m = c->transforms.mvp.matrix.m;
992
993    GLfixed m[16];
994    memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m));
995
996    do {
997        const GLfixed rx = vp[0];
998        const GLfixed ry = vp[1];
999        const GLfixed rz = vp[2];
1000        vp += stride;
1001        v->index = first++;
1002        v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]);
1003        v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
1004        v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
1005        v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
1006
1007        const GLfixed w = v->clip.w;
1008        uint32_t clip = 0;
1009        if (v->clip.x < -w)   clip |= vertex_t::CLIP_L;
1010        if (v->clip.x >  w)   clip |= vertex_t::CLIP_R;
1011        if (v->clip.y < -w)   clip |= vertex_t::CLIP_B;
1012        if (v->clip.y >  w)   clip |= vertex_t::CLIP_T;
1013        if (v->clip.z < -w)   clip |= vertex_t::CLIP_N;
1014        if (v->clip.z >  w)   clip |= vertex_t::CLIP_F;
1015        v->flags = clip;
1016        c->arrays.cull &= clip;
1017
1018        //c->arrays.perspective(c, v);
1019        v++;
1020    } while (--count);
1021}
1022*/
1023
1024// ----------------------------------------------------------------------------
1025#if 0
1026#pragma mark -
1027#pragma mark clippers
1028#endif
1029
1030static void clipVec4(vec4_t& nv,
1031        GLfixed t, const vec4_t& s, const vec4_t& p)
1032{
1033    for (int i=0; i<4 ; i++)
1034        nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28);
1035}
1036
1037static void clipVertex(ogles_context_t* c, vertex_t* nv,
1038        GLfixed t, const vertex_t* s, const vertex_t* p)
1039{
1040    clipVec4(nv->clip, t, s->clip, p->clip);
1041    nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28);
1042    ogles_vertex_project(c, nv);
1043    nv->flags |=  vertex_t::LIT | vertex_t::EYE | vertex_t::TT;
1044    nv->flags &= ~vertex_t::CLIP_ALL;
1045}
1046
1047static void clipVertexC(ogles_context_t* c, vertex_t* nv,
1048        GLfixed t, const vertex_t* s, const vertex_t* p)
1049{
1050    clipVec4(nv->color, t, s->color, p->color);
1051    clipVertex(c, nv, t, s, p);
1052}
1053
1054static void clipVertexT(ogles_context_t* c, vertex_t* nv,
1055        GLfixed t, const vertex_t* s, const vertex_t* p)
1056{
1057    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
1058        if (c->rasterizer.state.texture[i].enable)
1059            clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]);
1060    }
1061    clipVertex(c, nv, t, s, p);
1062}
1063
1064static void clipVertexAll(ogles_context_t* c, vertex_t* nv,
1065        GLfixed t, const vertex_t* s, const vertex_t* p)
1066{
1067    clipVec4(nv->color, t, s->color, p->color);
1068    clipVertexT(c, nv, t, s, p);
1069}
1070
1071static void clipEye(ogles_context_t* c, vertex_t* nv,
1072        GLfixed t, const vertex_t* s, const vertex_t* p)
1073{
1074    nv->clear();
1075    c->arrays.clipVertex(c, nv, t, p, s);
1076    clipVec4(nv->eye, t, s->eye, p->eye);
1077}
1078
1079// ----------------------------------------------------------------------------
1080#if 0
1081#pragma mark -
1082#endif
1083
1084void validate_arrays(ogles_context_t* c, GLenum mode)
1085{
1086    uint32_t enables = c->rasterizer.state.enables;
1087
1088    // Perspective correction is not need if Ortho transform, but
1089    // the user can still provide the w coordinate manually, so we can't
1090    // automatically turn it off (in fact we could when the 4th coordinate
1091    // is not spcified in the vertex array).
1092    // W interpolation is never needed for points.
1093    GLboolean perspective =
1094        c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS);
1095    c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective);
1096
1097    // set anti-aliasing
1098    GLboolean smooth = GL_FALSE;
1099    switch (mode) {
1100    case GL_POINTS:
1101        smooth = c->point.smooth;
1102        break;
1103    case GL_LINES:
1104    case GL_LINE_LOOP:
1105    case GL_LINE_STRIP:
1106        smooth = c->line.smooth;
1107        break;
1108    }
1109    if (((enables & GGL_ENABLE_AA)?1:0) != smooth)
1110        c->rasterizer.procs.enableDisable(c, GGL_AA, smooth);
1111
1112    // set the shade model for this primitive
1113    c->rasterizer.procs.shadeModel(c,
1114            (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel);
1115
1116    // compute all the matrices we'll need...
1117    uint32_t want =
1118            transform_state_t::MVP |
1119            transform_state_t::VIEWPORT;
1120    if (c->lighting.enable) { // needs normal transforms and eye coords
1121        want |= transform_state_t::MVUI;
1122        want |= transform_state_t::MODELVIEW;
1123    }
1124    if (enables & GGL_ENABLE_TMUS) { // needs texture transforms
1125        want |= transform_state_t::TEXTURE;
1126    }
1127    if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) {
1128        want |= transform_state_t::MODELVIEW; // needs eye coords
1129    }
1130    ogles_validate_transform(c, want);
1131
1132    // textures...
1133    if (enables & GGL_ENABLE_TMUS)
1134        ogles_validate_texture(c);
1135
1136    // vertex compilers
1137    c->arrays.compileElement = compileElement__generic;
1138    c->arrays.compileElements = compileElements__generic;
1139
1140    // vertex transform
1141    c->arrays.mvp_transform =
1142        c->transforms.mvp.pointv[c->arrays.vertex.size - 2];
1143
1144    c->arrays.mv_transform =
1145        c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2];
1146
1147    /*
1148     * ***********************************************************************
1149     *  pick fetchers
1150     * ***********************************************************************
1151     */
1152
1153    array_machine_t& am = c->arrays;
1154    am.vertex.fetch = fetchNop;
1155    am.normal.fetch = currentNormal;
1156    am.color.fetch = currentColor;
1157
1158    if (am.vertex.enable) {
1159        am.vertex.resolve();
1160        if (am.vertex.bo || am.vertex.pointer) {
1161            am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF];
1162        }
1163    }
1164
1165    if (am.normal.enable) {
1166        am.normal.resolve();
1167        if (am.normal.bo || am.normal.pointer) {
1168            am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF];
1169        }
1170    }
1171
1172    if (am.color.enable) {
1173        am.color.resolve();
1174        if (c->lighting.enable) {
1175            if (am.color.bo || am.color.pointer) {
1176                am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF];
1177            }
1178        } else {
1179            if (am.color.bo || am.color.pointer) {
1180                am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF];
1181            }
1182        }
1183    }
1184
1185    int activeTmuCount = 0;
1186    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
1187        am.texture[i].fetch = currentTexCoord;
1188        if (c->rasterizer.state.texture[i].enable) {
1189
1190            // texture fetchers...
1191            if (am.texture[i].enable) {
1192                am.texture[i].resolve();
1193                if (am.texture[i].bo || am.texture[i].pointer) {
1194                    am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF];
1195                }
1196            }
1197
1198            // texture transform...
1199            const int index = c->arrays.texture[i].size - 2;
1200            c->arrays.tex_transform[i] =
1201                c->transforms.texture[i].transform.pointv[index];
1202
1203            am.tmu = i;
1204            activeTmuCount++;
1205        }
1206    }
1207
1208    // pick the vertex-clipper
1209    uint32_t clipper = 0;
1210    // we must reload 'enables' here
1211    enables = c->rasterizer.state.enables;
1212    if (enables & GGL_ENABLE_SMOOTH)
1213        clipper |= 1;   // we need to interpolate colors
1214    if (enables & GGL_ENABLE_TMUS)
1215        clipper |= 2;   // we need to interpolate textures
1216    switch (clipper) {
1217    case 0: c->arrays.clipVertex = clipVertex;      break;
1218    case 1: c->arrays.clipVertex = clipVertexC;     break;
1219    case 2: c->arrays.clipVertex = clipVertexT;     break;
1220    case 3: c->arrays.clipVertex = clipVertexAll;   break;
1221    }
1222    c->arrays.clipEye = clipEye;
1223
1224    // pick the primitive rasterizer
1225    ogles_validate_primitives(c);
1226}
1227
1228// ----------------------------------------------------------------------------
1229}; // namespace android
1230// ----------------------------------------------------------------------------
1231
1232using namespace android;
1233
1234#if 0
1235#pragma mark -
1236#pragma mark array API
1237#endif
1238
1239void glVertexPointer(
1240    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
1241{
1242    ogles_context_t* c = ogles_context_t::get();
1243    if (size<2 || size>4 || stride<0) {
1244        ogles_error(c, GL_INVALID_VALUE);
1245        return;
1246    }
1247    switch (type) {
1248    case GL_BYTE:
1249    case GL_SHORT:
1250    case GL_FIXED:
1251    case GL_FLOAT:
1252        break;
1253    default:
1254        ogles_error(c, GL_INVALID_ENUM);
1255        return;
1256    }
1257    c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0);
1258}
1259
1260void glColorPointer(
1261    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
1262{
1263    ogles_context_t* c = ogles_context_t::get();
1264    if (size!=4 || stride<0) {
1265        ogles_error(c, GL_INVALID_VALUE);
1266        return;
1267    }
1268    switch (type) {
1269    case GL_UNSIGNED_BYTE:
1270    case GL_FIXED:
1271    case GL_FLOAT:
1272        break;
1273    default:
1274        ogles_error(c, GL_INVALID_ENUM);
1275        return;
1276    }
1277    c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0);
1278}
1279
1280void glNormalPointer(
1281    GLenum type, GLsizei stride, const GLvoid *pointer)
1282{
1283    ogles_context_t* c = ogles_context_t::get();
1284    if (stride<0) {
1285        ogles_error(c, GL_INVALID_VALUE);
1286        return;
1287    }
1288    switch (type) {
1289    case GL_BYTE:
1290    case GL_SHORT:
1291    case GL_FIXED:
1292    case GL_FLOAT:
1293        break;
1294    default:
1295        ogles_error(c, GL_INVALID_ENUM);
1296        return;
1297    }
1298    c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0);
1299}
1300
1301void glTexCoordPointer(
1302    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
1303{
1304    ogles_context_t* c = ogles_context_t::get();
1305    if (size<2 || size>4 || stride<0) {
1306        ogles_error(c, GL_INVALID_VALUE);
1307        return;
1308    }
1309    switch (type) {
1310    case GL_BYTE:
1311    case GL_SHORT:
1312    case GL_FIXED:
1313    case GL_FLOAT:
1314        break;
1315    default:
1316        ogles_error(c, GL_INVALID_ENUM);
1317        return;
1318    }
1319    const int tmu = c->arrays.activeTexture;
1320    c->arrays.texture[tmu].init(size, type, stride, pointer,
1321            c->arrays.array_buffer, 0);
1322}
1323
1324
1325void glEnableClientState(GLenum array) {
1326    ogles_context_t* c = ogles_context_t::get();
1327    enableDisableClientState(c, array, true);
1328}
1329
1330void glDisableClientState(GLenum array) {
1331    ogles_context_t* c = ogles_context_t::get();
1332    enableDisableClientState(c, array, false);
1333}
1334
1335void glClientActiveTexture(GLenum texture)
1336{
1337    ogles_context_t* c = ogles_context_t::get();
1338    if (texture<GL_TEXTURE0 || texture>=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) {
1339        ogles_error(c, GL_INVALID_ENUM);
1340        return;
1341    }
1342    c->arrays.activeTexture = texture - GL_TEXTURE0;
1343}
1344
1345void glDrawArrays(GLenum mode, GLint first, GLsizei count)
1346{
1347    ogles_context_t* c = ogles_context_t::get();
1348    if (count<0) {
1349        ogles_error(c, GL_INVALID_VALUE);
1350        return;
1351    }
1352    switch (mode) {
1353    case GL_POINTS:
1354    case GL_LINE_STRIP:
1355    case GL_LINE_LOOP:
1356    case GL_LINES:
1357    case GL_TRIANGLE_STRIP:
1358    case GL_TRIANGLE_FAN:
1359    case GL_TRIANGLES:
1360        break;
1361    default:
1362        ogles_error(c, GL_INVALID_ENUM);
1363        return;
1364    }
1365
1366    if (count == 0 || !c->arrays.vertex.enable)
1367        return;
1368    if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
1369        return; // all triangles are culled
1370
1371
1372    validate_arrays(c, mode);
1373
1374    const uint32_t enables = c->rasterizer.state.enables;
1375    if (enables & GGL_ENABLE_TMUS)
1376        ogles_lock_textures(c);
1377
1378    drawArraysPrims[mode](c, first, count);
1379
1380    if (enables & GGL_ENABLE_TMUS)
1381        ogles_unlock_textures(c);
1382
1383#if VC_CACHE_STATISTICS
1384    c->vc.total = count;
1385    c->vc.dump_stats(mode);
1386#endif
1387}
1388
1389void glDrawElements(
1390    GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
1391{
1392    ogles_context_t* c = ogles_context_t::get();
1393    if (count<0) {
1394        ogles_error(c, GL_INVALID_VALUE);
1395        return;
1396    }
1397    switch (mode) {
1398    case GL_POINTS:
1399    case GL_LINE_STRIP:
1400    case GL_LINE_LOOP:
1401    case GL_LINES:
1402    case GL_TRIANGLE_STRIP:
1403    case GL_TRIANGLE_FAN:
1404    case GL_TRIANGLES:
1405        break;
1406    default:
1407        ogles_error(c, GL_INVALID_ENUM);
1408        return;
1409    }
1410    switch (type) {
1411    case GL_UNSIGNED_BYTE:
1412    case GL_UNSIGNED_SHORT:
1413        c->arrays.indicesType = type;
1414        break;
1415    default:
1416        ogles_error(c, GL_INVALID_ENUM);
1417        return;
1418    }
1419    if (count == 0 || !c->arrays.vertex.enable)
1420        return;
1421    if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
1422        return; // all triangles are culled
1423
1424    // clear the vertex-cache
1425    c->vc.clear();
1426    validate_arrays(c, mode);
1427
1428    // if indices are in a buffer object, the pointer is treated as an
1429    // offset in that buffer.
1430    if (c->arrays.element_array_buffer) {
1431        indices = c->arrays.element_array_buffer->data + uintptr_t(indices);
1432    }
1433
1434    const uint32_t enables = c->rasterizer.state.enables;
1435    if (enables & GGL_ENABLE_TMUS)
1436        ogles_lock_textures(c);
1437
1438    drawElementsPrims[mode](c, count, indices);
1439
1440    if (enables & GGL_ENABLE_TMUS)
1441        ogles_unlock_textures(c);
1442
1443
1444#if VC_CACHE_STATISTICS
1445    c->vc.total = count;
1446    c->vc.dump_stats(mode);
1447#endif
1448}
1449
1450// ----------------------------------------------------------------------------
1451// buffers
1452// ----------------------------------------------------------------------------
1453
1454void glBindBuffer(GLenum target, GLuint buffer)
1455{
1456    ogles_context_t* c = ogles_context_t::get();
1457    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
1458        ogles_error(c, GL_INVALID_ENUM);
1459        return;
1460    }
1461    // create a buffer object, or bind an existing one
1462    buffer_t const* bo = 0;
1463    if (buffer) {
1464        bo = c->bufferObjectManager->bind(buffer);
1465        if (!bo) {
1466            ogles_error(c, GL_OUT_OF_MEMORY);
1467            return;
1468        }
1469    }
1470    ((target == GL_ARRAY_BUFFER) ?
1471            c->arrays.array_buffer : c->arrays.element_array_buffer) = bo;
1472}
1473
1474void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
1475{
1476    ogles_context_t* c = ogles_context_t::get();
1477    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
1478        ogles_error(c, GL_INVALID_ENUM);
1479        return;
1480    }
1481    if (size<0) {
1482        ogles_error(c, GL_INVALID_VALUE);
1483        return;
1484    }
1485    if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) {
1486        ogles_error(c, GL_INVALID_ENUM);
1487        return;
1488    }
1489    buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
1490            c->arrays.array_buffer : c->arrays.element_array_buffer);
1491
1492    if (bo == 0) {
1493        // can't modify buffer 0
1494        ogles_error(c, GL_INVALID_OPERATION);
1495        return;
1496    }
1497
1498    buffer_t* edit_bo = const_cast<buffer_t*>(bo);
1499    if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) {
1500        ogles_error(c, GL_OUT_OF_MEMORY);
1501        return;
1502    }
1503    if (data) {
1504        memcpy(bo->data, data, size);
1505    }
1506}
1507
1508void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
1509{
1510    ogles_context_t* c = ogles_context_t::get();
1511    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
1512        ogles_error(c, GL_INVALID_ENUM);
1513        return;
1514    }
1515    if (offset<0 || size<0 || data==0) {
1516        ogles_error(c, GL_INVALID_VALUE);
1517        return;
1518    }
1519    buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
1520            c->arrays.array_buffer : c->arrays.element_array_buffer);
1521
1522    if (bo == 0) {
1523        // can't modify buffer 0
1524        ogles_error(c, GL_INVALID_OPERATION);
1525        return;
1526    }
1527    if (offset+size > bo->size) {
1528        ogles_error(c, GL_INVALID_VALUE);
1529        return;
1530    }
1531    memcpy(bo->data + offset, data, size);
1532}
1533
1534void glDeleteBuffers(GLsizei n, const GLuint* buffers)
1535{
1536    ogles_context_t* c = ogles_context_t::get();
1537    if (n<0) {
1538        ogles_error(c, GL_INVALID_VALUE);
1539        return;
1540    }
1541
1542    for (int i=0 ; i<n ; i++) {
1543        GLuint name = buffers[i];
1544        if (name) {
1545            // unbind bound deleted buffers...
1546            if (c->arrays.element_array_buffer) {
1547                if (c->arrays.element_array_buffer->name == name) {
1548                    c->arrays.element_array_buffer = 0;
1549                }
1550            }
1551            if (c->arrays.array_buffer) {
1552                if (c->arrays.array_buffer->name == name) {
1553                    c->arrays.array_buffer = 0;
1554                }
1555            }
1556            if (c->arrays.vertex.bo) {
1557                if (c->arrays.vertex.bo->name == name) {
1558                    c->arrays.vertex.bo = 0;
1559                }
1560            }
1561            if (c->arrays.normal.bo) {
1562                if (c->arrays.normal.bo->name == name) {
1563                    c->arrays.normal.bo = 0;
1564                }
1565            }
1566            if (c->arrays.color.bo) {
1567                if (c->arrays.color.bo->name == name) {
1568                    c->arrays.color.bo = 0;
1569                }
1570            }
1571            for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
1572                if (c->arrays.texture[t].bo) {
1573                    if (c->arrays.texture[t].bo->name == name) {
1574                        c->arrays.texture[t].bo = 0;
1575                    }
1576                }
1577            }
1578        }
1579    }
1580    c->bufferObjectManager->deleteBuffers(n, buffers);
1581    c->bufferObjectManager->recycleTokens(n, buffers);
1582}
1583
1584void glGenBuffers(GLsizei n, GLuint* buffers)
1585{
1586    ogles_context_t* c = ogles_context_t::get();
1587    if (n<0) {
1588        ogles_error(c, GL_INVALID_VALUE);
1589        return;
1590    }
1591    c->bufferObjectManager->getToken(n, buffers);
1592}
1593