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