| /* |
| ** Copyright 2006, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include "context.h" |
| #include "fp.h" |
| #include "state.h" |
| #include "matrix.h" |
| #include "vertex.h" |
| #include "light.h" |
| #include "primitives.h" |
| #include "texture.h" |
| #include "BufferObjectManager.h" |
| |
| // ---------------------------------------------------------------------------- |
| |
| #define VC_CACHE_STATISTICS 0 |
| #define VC_CACHE_TYPE_NONE 0 |
| #define VC_CACHE_TYPE_INDEXED 1 |
| #define VC_CACHE_TYPE_LRU 2 |
| #define VC_CACHE_TYPE VC_CACHE_TYPE_INDEXED |
| |
| #if VC_CACHE_STATISTICS |
| #include <utils/Timers.h> |
| #endif |
| |
| // ---------------------------------------------------------------------------- |
| |
| namespace android { |
| |
| static void validate_arrays(ogles_context_t* c, GLenum mode); |
| |
| static void compileElements__generic(ogles_context_t*, |
| vertex_t*, GLint, GLsizei); |
| static void compileElement__generic(ogles_context_t*, |
| vertex_t*, GLint); |
| |
| static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei); |
| static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei); |
| static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei); |
| static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei); |
| static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei); |
| static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei); |
| static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei); |
| |
| static void drawIndexedPrimitivesPoints(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| static void drawIndexedPrimitivesLineStrip(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| static void drawIndexedPrimitivesLineLoop(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| static void drawIndexedPrimitivesLines(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| static void drawIndexedPrimitivesTriangleFan(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| static void drawIndexedPrimitivesTriangles(ogles_context_t*, |
| GLsizei, const GLvoid*); |
| |
| // ---------------------------------------------------------------------------- |
| |
| typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei); |
| static const arrays_prims_fct_t drawArraysPrims[] = { |
| drawPrimitivesPoints, |
| drawPrimitivesLines, |
| drawPrimitivesLineLoop, |
| drawPrimitivesLineStrip, |
| drawPrimitivesTriangles, |
| drawPrimitivesTriangleStrip, |
| drawPrimitivesTriangleFan |
| }; |
| |
| typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*); |
| static const elements_prims_fct_t drawElementsPrims[] = { |
| drawIndexedPrimitivesPoints, |
| drawIndexedPrimitivesLines, |
| drawIndexedPrimitivesLineLoop, |
| drawIndexedPrimitivesLineStrip, |
| drawIndexedPrimitivesTriangles, |
| drawIndexedPrimitivesTriangleStrip, |
| drawIndexedPrimitivesTriangleFan |
| }; |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| void ogles_init_array(ogles_context_t* c) |
| { |
| c->arrays.vertex.size = 4; |
| c->arrays.vertex.type = GL_FLOAT; |
| c->arrays.color.size = 4; |
| c->arrays.color.type = GL_FLOAT; |
| c->arrays.normal.size = 4; |
| c->arrays.normal.type = GL_FLOAT; |
| for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { |
| c->arrays.texture[i].size = 4; |
| c->arrays.texture[i].type = GL_FLOAT; |
| } |
| c->vc.init(); |
| |
| if (!c->vc.vBuffer) { |
| // this could have failed |
| ogles_error(c, GL_OUT_OF_MEMORY); |
| } |
| } |
| |
| void ogles_uninit_array(ogles_context_t* c) |
| { |
| c->vc.uninit(); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Array fetchers |
| #endif |
| |
| static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) { |
| memcpy(v, c->current.color.v, sizeof(vec4_t)); |
| } |
| static void currentColor_clamp(ogles_context_t* c, GLfixed* v, const GLvoid*) { |
| memcpy(v, c->currentColorClamped.v, sizeof(vec4_t)); |
| } |
| static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) { |
| memcpy(v, c->currentNormal.v, sizeof(vec3_t)); |
| } |
| static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) { |
| memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t)); |
| } |
| |
| |
| static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) { |
| } |
| static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) { |
| v[0] = gglIntToFixed(p[0]); |
| v[1] = gglIntToFixed(p[1]); |
| } |
| static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) { |
| v[0] = gglIntToFixed(p[0]); |
| v[1] = gglIntToFixed(p[1]); |
| } |
| static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) { |
| memcpy(v, p, 2*sizeof(GLfixed)); |
| } |
| static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) { |
| v[0] = gglFloatToFixed(p[0]); |
| v[1] = gglFloatToFixed(p[1]); |
| } |
| static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { |
| v[0] = gglIntToFixed(p[0]); |
| v[1] = gglIntToFixed(p[1]); |
| v[2] = gglIntToFixed(p[2]); |
| } |
| static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) { |
| v[0] = gglIntToFixed(p[0]); |
| v[1] = gglIntToFixed(p[1]); |
| v[2] = gglIntToFixed(p[2]); |
| } |
| static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { |
| memcpy(v, p, 3*sizeof(GLfixed)); |
| } |
| static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { |
| v[0] = gglFloatToFixed(p[0]); |
| v[1] = gglFloatToFixed(p[1]); |
| v[2] = gglFloatToFixed(p[2]); |
| } |
| static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) { |
| v[0] = gglIntToFixed(p[0]); |
| v[1] = gglIntToFixed(p[1]); |
| v[2] = gglIntToFixed(p[2]); |
| v[3] = gglIntToFixed(p[3]); |
| } |
| static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) { |
| v[0] = gglIntToFixed(p[0]); |
| v[1] = gglIntToFixed(p[1]); |
| v[2] = gglIntToFixed(p[2]); |
| v[3] = gglIntToFixed(p[3]); |
| } |
| static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { |
| memcpy(v, p, 4*sizeof(GLfixed)); |
| } |
| static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { |
| v[0] = gglFloatToFixed(p[0]); |
| v[1] = gglFloatToFixed(p[1]); |
| v[2] = gglFloatToFixed(p[2]); |
| v[3] = gglFloatToFixed(p[3]); |
| } |
| static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { |
| v[0] = GGL_UB_TO_X(p[0]); |
| v[1] = GGL_UB_TO_X(p[1]); |
| v[2] = GGL_UB_TO_X(p[2]); |
| v[3] = GGL_UB_TO_X(p[3]); |
| } |
| static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { |
| v[0] = gglClampx(p[0]); |
| v[1] = gglClampx(p[1]); |
| v[2] = gglClampx(p[2]); |
| v[3] = gglClampx(p[3]); |
| } |
| static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { |
| v[0] = gglClampx(gglFloatToFixed(p[0])); |
| v[1] = gglClampx(gglFloatToFixed(p[1])); |
| v[2] = gglClampx(gglFloatToFixed(p[2])); |
| v[3] = gglClampx(gglFloatToFixed(p[3])); |
| } |
| static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { |
| v[0] = GGL_UB_TO_X(p[0]); |
| v[1] = GGL_UB_TO_X(p[1]); |
| v[2] = GGL_UB_TO_X(p[2]); |
| v[3] = 0x10000; |
| } |
| static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { |
| v[0] = gglClampx(p[0]); |
| v[1] = gglClampx(p[1]); |
| v[2] = gglClampx(p[2]); |
| v[3] = 0x10000; |
| } |
| static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { |
| v[0] = gglClampx(gglFloatToFixed(p[0])); |
| v[1] = gglClampx(gglFloatToFixed(p[1])); |
| v[2] = gglClampx(gglFloatToFixed(p[2])); |
| v[3] = 0x10000; |
| } |
| static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { |
| v[0] = GGL_B_TO_X(p[0]); |
| v[1] = GGL_B_TO_X(p[1]); |
| v[2] = GGL_B_TO_X(p[2]); |
| } |
| static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) { |
| v[0] = GGL_S_TO_X(p[0]); |
| v[1] = GGL_S_TO_X(p[1]); |
| v[2] = GGL_S_TO_X(p[2]); |
| } |
| |
| typedef array_t::fetcher_t fn_t; |
| |
| static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x} |
| { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, |
| (fn_t)fetch3f, 0, 0, 0, 0, 0, |
| (fn_t)fetch3x }, |
| { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, |
| (fn_t)fetch4f, 0, 0, 0, 0, 0, |
| (fn_t)fetch4x }, |
| }; |
| static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x} |
| { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, |
| (fn_t)fetchClamp3f, 0, 0, 0, 0, 0, |
| (fn_t)fetchClamp3x }, |
| { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, |
| (fn_t)fetchClamp4f, 0, 0, 0, 0, 0, |
| (fn_t)fetchClamp4x }, |
| }; |
| static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x} |
| { (fn_t)fetchExpand3b, 0, |
| (fn_t)fetchExpand3s, 0, 0, 0, |
| (fn_t)fetch3f, 0, 0, 0, 0, 0, |
| (fn_t)fetch3x }, |
| }; |
| static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} |
| { (fn_t)fetch2b, 0, |
| (fn_t)fetch2s, 0, 0, 0, |
| (fn_t)fetch2f, 0, 0, 0, 0, 0, |
| (fn_t)fetch3x }, |
| { (fn_t)fetch3b, 0, |
| (fn_t)fetch3s, 0, 0, 0, |
| (fn_t)fetch3f, 0, 0, 0, 0, 0, |
| (fn_t)fetch3x }, |
| { (fn_t)fetch4b, 0, |
| (fn_t)fetch4s, 0, 0, 0, |
| (fn_t)fetch4f, 0, 0, 0, 0, 0, |
| (fn_t)fetch4x } |
| }; |
| static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} |
| { (fn_t)fetch2b, 0, |
| (fn_t)fetch2s, 0, 0, 0, |
| (fn_t)fetch2f, 0, 0, 0, 0, 0, |
| (fn_t)fetch2x }, |
| { (fn_t)fetch3b, 0, |
| (fn_t)fetch3s, 0, 0, 0, |
| (fn_t)fetch3f, 0, 0, 0, 0, 0, |
| (fn_t)fetch3x }, |
| { (fn_t)fetch4b, 0, |
| (fn_t)fetch4s, 0, 0, 0, |
| (fn_t)fetch4f, 0, 0, 0, 0, 0, |
| (fn_t)fetch4x } |
| }; |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark array_t |
| #endif |
| |
| void array_t::init( |
| GLint size, GLenum type, GLsizei stride, |
| const GLvoid *pointer, const buffer_t* bo, GLsizei count) |
| { |
| if (!stride) { |
| stride = size; |
| switch (type) { |
| case GL_SHORT: |
| case GL_UNSIGNED_SHORT: |
| stride *= 2; |
| break; |
| case GL_FLOAT: |
| case GL_FIXED: |
| stride *= 4; |
| break; |
| } |
| } |
| this->size = size; |
| this->type = type; |
| this->stride = stride; |
| this->pointer = pointer; |
| this->bo = bo; |
| this->bounds = count; |
| } |
| |
| inline void array_t::resolve() |
| { |
| physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark vertex_cache_t |
| #endif |
| |
| void vertex_cache_t::init() |
| { |
| // make sure the size of vertex_t allows cache-line alignment |
| CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize; |
| |
| const int align = 32; |
| const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; |
| const size_t size = s*sizeof(vertex_t) + align; |
| base = malloc(size); |
| if (base) { |
| memset(base, 0, size); |
| vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1)); |
| vCache = vBuffer + VERTEX_BUFFER_SIZE; |
| sequence = 0; |
| } |
| } |
| |
| void vertex_cache_t::uninit() |
| { |
| free(base); |
| base = vBuffer = vCache = 0; |
| } |
| |
| void vertex_cache_t::clear() |
| { |
| #if VC_CACHE_STATISTICS |
| startTime = systemTime(SYSTEM_TIME_THREAD); |
| total = 0; |
| misses = 0; |
| #endif |
| |
| #if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU |
| vertex_t* v = vBuffer; |
| size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; |
| do { |
| v->mru = 0; |
| v++; |
| } while (--count); |
| #endif |
| |
| sequence += INDEX_SEQ; |
| if (sequence >= 0x80000000LU) { |
| sequence = INDEX_SEQ; |
| vertex_t* v = vBuffer; |
| size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; |
| do { |
| v->index = 0; |
| v++; |
| } while (--count); |
| } |
| } |
| |
| void vertex_cache_t::dump_stats(GLenum mode) |
| { |
| #if VC_CACHE_STATISTICS |
| nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime; |
| uint32_t hits = total - misses; |
| uint32_t prim_count; |
| switch (mode) { |
| case GL_POINTS: prim_count = total; break; |
| case GL_LINE_STRIP: prim_count = total - 1; break; |
| case GL_LINE_LOOP: prim_count = total - 1; break; |
| case GL_LINES: prim_count = total / 2; break; |
| case GL_TRIANGLE_STRIP: prim_count = total - 2; break; |
| case GL_TRIANGLE_FAN: prim_count = total - 2; break; |
| case GL_TRIANGLES: prim_count = total / 3; break; |
| default: return; |
| } |
| printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%," |
| " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n", |
| total, hits, misses, (hits*100)/total, |
| prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time), |
| float(misses) / prim_count); |
| #endif |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| static __attribute__((noinline)) |
| void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable) |
| { |
| const int tmu = c->arrays.activeTexture; |
| array_t* a; |
| switch (array) { |
| case GL_COLOR_ARRAY: a = &c->arrays.color; break; |
| case GL_NORMAL_ARRAY: a = &c->arrays.normal; break; |
| case GL_TEXTURE_COORD_ARRAY: a = &c->arrays.texture[tmu]; break; |
| case GL_VERTEX_ARRAY: a = &c->arrays.vertex; break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| a->enable = enable ? GL_TRUE : GL_FALSE; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Vertex Cache |
| #endif |
| |
| static __attribute__((noinline)) |
| vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index) |
| { |
| #if VC_CACHE_STATISTICS |
| c->vc.misses++; |
| #endif |
| if (ggl_unlikely(v->locked)) { |
| // we're just looking for an entry in the cache that is not locked. |
| // and we know that there cannot be more than 2 locked entries |
| // because a triangle needs at most 3 vertices. |
| // We never use the first and second entries because they might be in |
| // use by the striper or faner. Any other entry will do as long as |
| // it's not locked. |
| // We compute directly the index of a "free" entry from the locked |
| // state of v[2] and v[3]. |
| v = c->vc.vBuffer + 2; |
| v += v[0].locked | (v[1].locked<<1); |
| } |
| // note: compileElement clears v->flags |
| c->arrays.compileElement(c, v, index); |
| v->locked = 1; |
| return v; |
| } |
| |
| static __attribute__((noinline)) |
| vertex_t* fetch_vertex(ogles_context_t* c, size_t index) |
| { |
| index |= c->vc.sequence; |
| |
| #if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED |
| |
| vertex_t* const v = c->vc.vCache + |
| (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1)); |
| |
| if (ggl_likely(v->index == index)) { |
| v->locked = 1; |
| return v; |
| } |
| return cache_vertex(c, v, index); |
| |
| #elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU |
| |
| vertex_t* v = c->vc.vCache + |
| (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2; |
| |
| // always record LRU in v[0] |
| if (ggl_likely(v[0].index == index)) { |
| v[0].locked = 1; |
| v[0].mru = 0; |
| return &v[0]; |
| } |
| |
| if (ggl_likely(v[1].index == index)) { |
| v[1].locked = 1; |
| v[0].mru = 1; |
| return &v[1]; |
| } |
| |
| const int lru = 1 - v[0].mru; |
| v[0].mru = lru; |
| return cache_vertex(c, &v[lru], index); |
| |
| #elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE |
| |
| // just for debugging... |
| vertex_t* v = c->vc.vBuffer + 2; |
| return cache_vertex(c, v, index); |
| |
| #endif |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Primitive Assembly |
| #endif |
| |
| void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count) |
| { |
| if (ggl_unlikely(count < 1)) |
| return; |
| |
| // vertex cache size must be multiple of 1 |
| const GLsizei vcs = |
| (vertex_cache_t::VERTEX_BUFFER_SIZE + |
| vertex_cache_t::VERTEX_CACHE_SIZE); |
| do { |
| vertex_t* v = c->vc.vBuffer; |
| GLsizei num = count > vcs ? vcs : count; |
| c->arrays.cull = vertex_t::CLIP_ALL; |
| c->arrays.compileElements(c, v, first, num); |
| first += num; |
| count -= num; |
| if (!c->arrays.cull) { |
| // quick/trivial reject of the whole batch |
| do { |
| const uint32_t cc = v[0].flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderPoint(c, v); |
| v++; |
| num--; |
| } while (num); |
| } |
| } while (count); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count) |
| { |
| if (ggl_unlikely(count < 2)) |
| return; |
| |
| vertex_t *v, *v0, *v1; |
| c->arrays.cull = vertex_t::CLIP_ALL; |
| c->arrays.compileElement(c, c->vc.vBuffer, first); |
| first += 1; |
| count -= 1; |
| |
| // vertex cache size must be multiple of 1 |
| const GLsizei vcs = |
| (vertex_cache_t::VERTEX_BUFFER_SIZE + |
| vertex_cache_t::VERTEX_CACHE_SIZE - 1); |
| do { |
| v0 = c->vc.vBuffer + 0; |
| v = c->vc.vBuffer + 1; |
| GLsizei num = count > vcs ? vcs : count; |
| c->arrays.compileElements(c, v, first, num); |
| first += num; |
| count -= num; |
| if (!c->arrays.cull) { |
| // quick/trivial reject of the whole batch |
| do { |
| v1 = v++; |
| const uint32_t cc = v0->flags & v1->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v0, v1); |
| v0 = v1; |
| num--; |
| } while (num); |
| } |
| // copy back the last processed vertex |
| c->vc.vBuffer[0] = *v0; |
| c->arrays.cull = v0->flags & vertex_t::CLIP_ALL; |
| } while (count); |
| } |
| |
| void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count) |
| { |
| if (ggl_unlikely(count < 2)) |
| return; |
| drawPrimitivesLineStrip(c, first, count); |
| if (ggl_likely(count >= 3)) { |
| vertex_t* v0 = c->vc.vBuffer; |
| vertex_t* v1 = c->vc.vBuffer + 1; |
| c->arrays.compileElement(c, v1, first); |
| const uint32_t cc = v0->flags & v1->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v0, v1); |
| } |
| } |
| |
| void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count) |
| { |
| if (ggl_unlikely(count < 2)) |
| return; |
| |
| // vertex cache size must be multiple of 2 |
| const GLsizei vcs = |
| ((vertex_cache_t::VERTEX_BUFFER_SIZE + |
| vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2; |
| do { |
| vertex_t* v = c->vc.vBuffer; |
| GLsizei num = count > vcs ? vcs : count; |
| c->arrays.cull = vertex_t::CLIP_ALL; |
| c->arrays.compileElements(c, v, first, num); |
| first += num; |
| count -= num; |
| if (!c->arrays.cull) { |
| // quick/trivial reject of the whole batch |
| num -= 2; |
| do { |
| const uint32_t cc = v[0].flags & v[1].flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v, v+1); |
| v += 2; |
| num -= 2; |
| } while (num >= 0); |
| } |
| } while (count >= 2); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c, |
| GLint first, GLsizei count, int winding) |
| { |
| // winding == 2 : fan |
| // winding == 1 : strip |
| |
| if (ggl_unlikely(count < 3)) |
| return; |
| |
| vertex_t *v, *v0, *v1, *v2; |
| c->arrays.cull = vertex_t::CLIP_ALL; |
| c->arrays.compileElements(c, c->vc.vBuffer, first, 2); |
| first += 2; |
| count -= 2; |
| |
| // vertex cache size must be multiple of 2. This is extremely important |
| // because it allows us to preserve the same winding when the whole |
| // batch is culled. We also need 2 extra vertices in the array, because |
| // we always keep the two first ones. |
| const GLsizei vcs = |
| ((vertex_cache_t::VERTEX_BUFFER_SIZE + |
| vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2; |
| do { |
| v0 = c->vc.vBuffer + 0; |
| v1 = c->vc.vBuffer + 1; |
| v = c->vc.vBuffer + 2; |
| GLsizei num = count > vcs ? vcs : count; |
| c->arrays.compileElements(c, v, first, num); |
| first += num; |
| count -= num; |
| if (!c->arrays.cull) { |
| // quick/trivial reject of the whole batch |
| do { |
| v2 = v++; |
| const uint32_t cc = v0->flags & v1->flags & v2->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderTriangle(c, v0, v1, v2); |
| swap(((winding^=1) ? v1 : v0), v2); |
| num--; |
| } while (num); |
| } |
| if (count) { |
| v0 = c->vc.vBuffer + 2 + num - 2; |
| v1 = c->vc.vBuffer + 2 + num - 1; |
| if ((winding&2) == 0) { |
| // for strips copy back the two last compiled vertices |
| c->vc.vBuffer[0] = *v0; |
| } |
| c->vc.vBuffer[1] = *v1; |
| c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL; |
| } |
| } while (count > 0); |
| } |
| |
| void drawPrimitivesTriangleStrip(ogles_context_t* c, |
| GLint first, GLsizei count) { |
| drawPrimitivesTriangleFanOrStrip(c, first, count, 1); |
| } |
| |
| void drawPrimitivesTriangleFan(ogles_context_t* c, |
| GLint first, GLsizei count) { |
| drawPrimitivesTriangleFanOrStrip(c, first, count, 2); |
| } |
| |
| void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count) |
| { |
| if (ggl_unlikely(count < 3)) |
| return; |
| |
| // vertex cache size must be multiple of 3 |
| const GLsizei vcs = |
| ((vertex_cache_t::VERTEX_BUFFER_SIZE + |
| vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3; |
| do { |
| vertex_t* v = c->vc.vBuffer; |
| GLsizei num = count > vcs ? vcs : count; |
| c->arrays.cull = vertex_t::CLIP_ALL; |
| c->arrays.compileElements(c, v, first, num); |
| first += num; |
| count -= num; |
| if (!c->arrays.cull) { |
| // quick/trivial reject of the whole batch |
| num -= 3; |
| do { |
| const uint32_t cc = v[0].flags & v[1].flags & v[2].flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderTriangle(c, v, v+1, v+2); |
| v += 3; |
| num -= 3; |
| } while (num >= 0); |
| } |
| } while (count >= 3); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| // this looks goofy, but gcc does a great job with this... |
| static inline unsigned int read_index(int type, const GLvoid*& p) { |
| unsigned int r; |
| if (type) { |
| r = *(const GLubyte*)p; |
| p = (const GLubyte*)p + 1; |
| } else { |
| r = *(const GLushort*)p; |
| p = (const GLushort*)p + 1; |
| } |
| return r; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| void drawIndexedPrimitivesPoints(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) |
| { |
| if (ggl_unlikely(count < 1)) |
| return; |
| const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); |
| do { |
| vertex_t * v = fetch_vertex(c, read_index(type, indices)); |
| if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL))) |
| c->prims.renderPoint(c, v); |
| v->locked = 0; |
| count--; |
| } while(count); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| void drawIndexedPrimitivesLineStrip(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) |
| { |
| if (ggl_unlikely(count < 2)) |
| return; |
| |
| vertex_t * const v = c->vc.vBuffer; |
| vertex_t* v0 = v; |
| vertex_t* v1; |
| |
| const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); |
| c->arrays.compileElement(c, v0, read_index(type, indices)); |
| count -= 1; |
| do { |
| v1 = fetch_vertex(c, read_index(type, indices)); |
| const uint32_t cc = v0->flags & v1->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v0, v1); |
| v0->locked = 0; |
| v0 = v1; |
| count--; |
| } while (count); |
| v1->locked = 0; |
| } |
| |
| void drawIndexedPrimitivesLineLoop(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) |
| { |
| if (ggl_unlikely(count <= 2)) { |
| drawIndexedPrimitivesLines(c, count, indices); |
| return; |
| } |
| |
| vertex_t * const v = c->vc.vBuffer; |
| vertex_t* v0 = v; |
| vertex_t* v1; |
| |
| const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); |
| c->arrays.compileElement(c, v0, read_index(type, indices)); |
| count -= 1; |
| do { |
| v1 = fetch_vertex(c, read_index(type, indices)); |
| const uint32_t cc = v0->flags & v1->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v0, v1); |
| v0->locked = 0; |
| v0 = v1; |
| count--; |
| } while (count); |
| v1->locked = 0; |
| |
| v1 = c->vc.vBuffer; |
| const uint32_t cc = v0->flags & v1->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v0, v1); |
| } |
| |
| void drawIndexedPrimitivesLines(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) |
| { |
| if (ggl_unlikely(count < 2)) |
| return; |
| |
| count -= 2; |
| const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); |
| do { |
| vertex_t* const v0 = fetch_vertex(c, read_index(type, indices)); |
| vertex_t* const v1 = fetch_vertex(c, read_index(type, indices)); |
| const uint32_t cc = v0->flags & v1->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderLine(c, v0, v1); |
| v0->locked = 0; |
| v1->locked = 0; |
| count -= 2; |
| } while (count >= 0); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices, int winding) |
| { |
| // winding == 2 : fan |
| // winding == 1 : strip |
| |
| if (ggl_unlikely(count < 3)) |
| return; |
| |
| vertex_t * const v = c->vc.vBuffer; |
| vertex_t* v0 = v; |
| vertex_t* v1 = v+1; |
| vertex_t* v2; |
| |
| const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); |
| c->arrays.compileElement(c, v0, read_index(type, indices)); |
| c->arrays.compileElement(c, v1, read_index(type, indices)); |
| count -= 2; |
| |
| // note: GCC 4.1.1 here makes a prety interesting optimization |
| // where it duplicates the loop below based on c->arrays.indicesType |
| |
| do { |
| v2 = fetch_vertex(c, read_index(type, indices)); |
| const uint32_t cc = v0->flags & v1->flags & v2->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderTriangle(c, v0, v1, v2); |
| vertex_t* & consumed = ((winding^=1) ? v1 : v0); |
| consumed->locked = 0; |
| consumed = v2; |
| count--; |
| } while (count); |
| v0->locked = v1->locked = 0; |
| v2->locked = 0; |
| } |
| |
| void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) { |
| drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1); |
| } |
| |
| void drawIndexedPrimitivesTriangleFan(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) { |
| drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2); |
| } |
| |
| void drawIndexedPrimitivesTriangles(ogles_context_t* c, |
| GLsizei count, const GLvoid *indices) |
| { |
| if (ggl_unlikely(count < 3)) |
| return; |
| |
| count -= 3; |
| if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) { |
| // This case is probably our most common case... |
| uint16_t const * p = (uint16_t const *)indices; |
| do { |
| vertex_t* const v0 = fetch_vertex(c, *p++); |
| vertex_t* const v1 = fetch_vertex(c, *p++); |
| vertex_t* const v2 = fetch_vertex(c, *p++); |
| const uint32_t cc = v0->flags & v1->flags & v2->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderTriangle(c, v0, v1, v2); |
| v0->locked = 0; |
| v1->locked = 0; |
| v2->locked = 0; |
| count -= 3; |
| } while (count >= 0); |
| } else { |
| uint8_t const * p = (uint8_t const *)indices; |
| do { |
| vertex_t* const v0 = fetch_vertex(c, *p++); |
| vertex_t* const v1 = fetch_vertex(c, *p++); |
| vertex_t* const v2 = fetch_vertex(c, *p++); |
| const uint32_t cc = v0->flags & v1->flags & v2->flags; |
| if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) |
| c->prims.renderTriangle(c, v0, v1, v2); |
| v0->locked = 0; |
| v1->locked = 0; |
| v2->locked = 0; |
| count -= 3; |
| } while (count >= 0); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Array compilers |
| #endif |
| |
| void compileElement__generic(ogles_context_t* c, |
| vertex_t* v, GLint first) |
| { |
| v->flags = 0; |
| v->index = first; |
| first &= vertex_cache_t::INDEX_MASK; |
| const GLubyte* vp = c->arrays.vertex.element(first); |
| v->obj.z = 0; |
| v->obj.w = 0x10000; |
| c->arrays.vertex.fetch(c, v->obj.v, vp); |
| c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj); |
| c->arrays.perspective(c, v); |
| } |
| |
| void compileElements__generic(ogles_context_t* c, |
| vertex_t* v, GLint first, GLsizei count) |
| { |
| const GLubyte* vp = c->arrays.vertex.element( |
| first & vertex_cache_t::INDEX_MASK); |
| const size_t stride = c->arrays.vertex.stride; |
| transform_t const* const mvp = &c->transforms.mvp; |
| do { |
| v->flags = 0; |
| v->index = first++; |
| v->obj.z = 0; |
| v->obj.w = 0x10000; |
| c->arrays.vertex.fetch(c, v->obj.v, vp); |
| c->arrays.mvp_transform(mvp, &v->clip, &v->obj); |
| c->arrays.perspective(c, v); |
| vp += stride; |
| v++; |
| } while (--count); |
| } |
| |
| /* |
| void compileElements__3x_full(ogles_context_t* c, |
| vertex_t* v, GLint first, GLsizei count) |
| { |
| const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first); |
| const size_t stride = c->arrays.vertex.stride / 4; |
| // const GLfixed* const& m = c->transforms.mvp.matrix.m; |
| |
| GLfixed m[16]; |
| memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m)); |
| |
| do { |
| const GLfixed rx = vp[0]; |
| const GLfixed ry = vp[1]; |
| const GLfixed rz = vp[2]; |
| vp += stride; |
| v->index = first++; |
| v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); |
| v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); |
| v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); |
| v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); |
| |
| const GLfixed w = v->clip.w; |
| uint32_t clip = 0; |
| if (v->clip.x < -w) clip |= vertex_t::CLIP_L; |
| if (v->clip.x > w) clip |= vertex_t::CLIP_R; |
| if (v->clip.y < -w) clip |= vertex_t::CLIP_B; |
| if (v->clip.y > w) clip |= vertex_t::CLIP_T; |
| if (v->clip.z < -w) clip |= vertex_t::CLIP_N; |
| if (v->clip.z > w) clip |= vertex_t::CLIP_F; |
| v->flags = clip; |
| c->arrays.cull &= clip; |
| |
| //c->arrays.perspective(c, v); |
| v++; |
| } while (--count); |
| } |
| */ |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark clippers |
| #endif |
| |
| static void clipVec4(vec4_t& nv, |
| GLfixed t, const vec4_t& s, const vec4_t& p) |
| { |
| for (int i=0; i<4 ; i++) |
| nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28); |
| } |
| |
| static void clipVertex(ogles_context_t* c, vertex_t* nv, |
| GLfixed t, const vertex_t* s, const vertex_t* p) |
| { |
| clipVec4(nv->clip, t, s->clip, p->clip); |
| nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28); |
| ogles_vertex_project(c, nv); |
| nv->flags |= vertex_t::LIT | vertex_t::EYE | vertex_t::TT; |
| nv->flags &= ~vertex_t::CLIP_ALL; |
| } |
| |
| static void clipVertexC(ogles_context_t* c, vertex_t* nv, |
| GLfixed t, const vertex_t* s, const vertex_t* p) |
| { |
| clipVec4(nv->color, t, s->color, p->color); |
| clipVertex(c, nv, t, s, p); |
| } |
| |
| static void clipVertexT(ogles_context_t* c, vertex_t* nv, |
| GLfixed t, const vertex_t* s, const vertex_t* p) |
| { |
| for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { |
| if (c->rasterizer.state.texture[i].enable) |
| clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]); |
| } |
| clipVertex(c, nv, t, s, p); |
| } |
| |
| static void clipVertexAll(ogles_context_t* c, vertex_t* nv, |
| GLfixed t, const vertex_t* s, const vertex_t* p) |
| { |
| clipVec4(nv->color, t, s->color, p->color); |
| clipVertexT(c, nv, t, s, p); |
| } |
| |
| static void clipEye(ogles_context_t* c, vertex_t* nv, |
| GLfixed t, const vertex_t* s, const vertex_t* p) |
| { |
| nv->clear(); |
| c->arrays.clipVertex(c, nv, t, p, s); |
| clipVec4(nv->eye, t, s->eye, p->eye); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| void validate_arrays(ogles_context_t* c, GLenum mode) |
| { |
| uint32_t enables = c->rasterizer.state.enables; |
| |
| // Perspective correction is not need if Ortho transform, but |
| // the user can still provide the w coordinate manually, so we can't |
| // automatically turn it off (in fact we could when the 4th coordinate |
| // is not spcified in the vertex array). |
| // W interpolation is never needed for points. |
| GLboolean perspective = |
| c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS); |
| c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective); |
| |
| // set anti-aliasing |
| GLboolean smooth = GL_FALSE; |
| switch (mode) { |
| case GL_POINTS: |
| smooth = c->point.smooth; |
| break; |
| case GL_LINES: |
| case GL_LINE_LOOP: |
| case GL_LINE_STRIP: |
| smooth = c->line.smooth; |
| break; |
| } |
| if (((enables & GGL_ENABLE_AA)?1:0) != smooth) |
| c->rasterizer.procs.enableDisable(c, GGL_AA, smooth); |
| |
| // set the shade model for this primitive |
| c->rasterizer.procs.shadeModel(c, |
| (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel); |
| |
| // compute all the matrices we'll need... |
| uint32_t want = |
| transform_state_t::MVP | |
| transform_state_t::VIEWPORT; |
| if (c->lighting.enable) { // needs normal transforms and eye coords |
| want |= transform_state_t::MVUI; |
| want |= transform_state_t::MODELVIEW; |
| } |
| if (enables & GGL_ENABLE_TMUS) { // needs texture transforms |
| want |= transform_state_t::TEXTURE; |
| } |
| if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) { |
| want |= transform_state_t::MODELVIEW; // needs eye coords |
| } |
| ogles_validate_transform(c, want); |
| |
| // textures... |
| if (enables & GGL_ENABLE_TMUS) |
| ogles_validate_texture(c); |
| |
| // vertex compilers |
| c->arrays.compileElement = compileElement__generic; |
| c->arrays.compileElements = compileElements__generic; |
| |
| // vertex transform |
| c->arrays.mvp_transform = |
| c->transforms.mvp.pointv[c->arrays.vertex.size - 2]; |
| |
| c->arrays.mv_transform = |
| c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2]; |
| |
| /* |
| * *********************************************************************** |
| * pick fetchers |
| * *********************************************************************** |
| */ |
| |
| array_machine_t& am = c->arrays; |
| am.vertex.fetch = fetchNop; |
| am.normal.fetch = currentNormal; |
| am.color.fetch = currentColor; |
| |
| if (am.vertex.enable) { |
| am.vertex.resolve(); |
| if (am.vertex.bo || am.vertex.pointer) { |
| am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF]; |
| } |
| } |
| |
| if (am.normal.enable) { |
| am.normal.resolve(); |
| if (am.normal.bo || am.normal.pointer) { |
| am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF]; |
| } |
| } |
| |
| if (am.color.enable) { |
| am.color.resolve(); |
| if (c->lighting.enable) { |
| if (am.color.bo || am.color.pointer) { |
| am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF]; |
| } |
| } else { |
| if (am.color.bo || am.color.pointer) { |
| am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF]; |
| } |
| } |
| } |
| |
| int activeTmuCount = 0; |
| for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { |
| am.texture[i].fetch = currentTexCoord; |
| if (c->rasterizer.state.texture[i].enable) { |
| |
| // texture fetchers... |
| if (am.texture[i].enable) { |
| am.texture[i].resolve(); |
| if (am.texture[i].bo || am.texture[i].pointer) { |
| am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF]; |
| } |
| } |
| |
| // texture transform... |
| const int index = c->arrays.texture[i].size - 2; |
| c->arrays.tex_transform[i] = |
| c->transforms.texture[i].transform.pointv[index]; |
| |
| am.tmu = i; |
| activeTmuCount++; |
| } |
| } |
| |
| // pick the vertex-clipper |
| uint32_t clipper = 0; |
| // we must reload 'enables' here |
| enables = c->rasterizer.state.enables; |
| if (enables & GGL_ENABLE_SMOOTH) |
| clipper |= 1; // we need to interpolate colors |
| if (enables & GGL_ENABLE_TMUS) |
| clipper |= 2; // we need to interpolate textures |
| switch (clipper) { |
| case 0: c->arrays.clipVertex = clipVertex; break; |
| case 1: c->arrays.clipVertex = clipVertexC; break; |
| case 2: c->arrays.clipVertex = clipVertexT; break; |
| case 3: c->arrays.clipVertex = clipVertexAll; break; |
| } |
| c->arrays.clipEye = clipEye; |
| |
| // pick the primitive rasterizer |
| ogles_validate_primitives(c); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| }; // namespace android |
| // ---------------------------------------------------------------------------- |
| |
| using namespace android; |
| |
| #if 0 |
| #pragma mark - |
| #pragma mark array API |
| #endif |
| |
| void glVertexPointer( |
| GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (size<2 || size>4 || stride<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| switch (type) { |
| case GL_BYTE: |
| case GL_SHORT: |
| case GL_FIXED: |
| case GL_FLOAT: |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0); |
| } |
| |
| void glColorPointer( |
| GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| // in theory ogles doesn't allow color arrays of size 3 |
| // but it is very useful to 'visualize' the normal array. |
| if (size<3 || size>4 || stride<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| case GL_FIXED: |
| case GL_FLOAT: |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0); |
| } |
| |
| void glNormalPointer( |
| GLenum type, GLsizei stride, const GLvoid *pointer) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (stride<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| switch (type) { |
| case GL_BYTE: |
| case GL_SHORT: |
| case GL_FIXED: |
| case GL_FLOAT: |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0); |
| } |
| |
| void glTexCoordPointer( |
| GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (size<2 || size>4 || stride<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| switch (type) { |
| case GL_BYTE: |
| case GL_SHORT: |
| case GL_FIXED: |
| case GL_FLOAT: |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| const int tmu = c->arrays.activeTexture; |
| c->arrays.texture[tmu].init(size, type, stride, pointer, |
| c->arrays.array_buffer, 0); |
| } |
| |
| |
| void glEnableClientState(GLenum array) { |
| ogles_context_t* c = ogles_context_t::get(); |
| enableDisableClientState(c, array, true); |
| } |
| |
| void glDisableClientState(GLenum array) { |
| ogles_context_t* c = ogles_context_t::get(); |
| enableDisableClientState(c, array, false); |
| } |
| |
| void glClientActiveTexture(GLenum texture) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (texture<GL_TEXTURE0 || texture>=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) { |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| c->arrays.activeTexture = texture - GL_TEXTURE0; |
| } |
| |
| void glDrawArrays(GLenum mode, GLint first, GLsizei count) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (count<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| switch (mode) { |
| case GL_POINTS: |
| case GL_LINE_STRIP: |
| case GL_LINE_LOOP: |
| case GL_LINES: |
| case GL_TRIANGLE_STRIP: |
| case GL_TRIANGLE_FAN: |
| case GL_TRIANGLES: |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| |
| if (count == 0 || !c->arrays.vertex.enable) |
| return; |
| if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) |
| return; // all triangles are culled |
| |
| validate_arrays(c, mode); |
| drawArraysPrims[mode](c, first, count); |
| |
| #if VC_CACHE_STATISTICS |
| c->vc.total = count; |
| c->vc.dump_stats(mode); |
| #endif |
| } |
| |
| void glDrawElements( |
| GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (count<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| switch (mode) { |
| case GL_POINTS: |
| case GL_LINE_STRIP: |
| case GL_LINE_LOOP: |
| case GL_LINES: |
| case GL_TRIANGLE_STRIP: |
| case GL_TRIANGLE_FAN: |
| case GL_TRIANGLES: |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| case GL_UNSIGNED_SHORT: |
| c->arrays.indicesType = type; |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| if (count == 0 || !c->arrays.vertex.enable) |
| return; |
| if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) |
| return; // all triangles are culled |
| |
| // clear the vertex-cache |
| c->vc.clear(); |
| validate_arrays(c, mode); |
| |
| // if indices are in a buffer object, the pointer is treated as an |
| // offset in that buffer. |
| if (c->arrays.element_array_buffer) { |
| indices = c->arrays.element_array_buffer->data + uintptr_t(indices); |
| } |
| |
| drawElementsPrims[mode](c, count, indices); |
| |
| #if VC_CACHE_STATISTICS |
| c->vc.total = count; |
| c->vc.dump_stats(mode); |
| #endif |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // buffers |
| // ---------------------------------------------------------------------------- |
| |
| void glBindBuffer(GLenum target, GLuint buffer) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| // create a buffer object, or bind an existing one |
| buffer_t const* bo = 0; |
| if (buffer) { |
| bo = c->bufferObjectManager->bind(buffer); |
| if (!bo) { |
| ogles_error(c, GL_OUT_OF_MEMORY); |
| return; |
| } |
| } |
| ((target == GL_ARRAY_BUFFER) ? |
| c->arrays.array_buffer : c->arrays.element_array_buffer) = bo; |
| } |
| |
| void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| if (size<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) { |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? |
| c->arrays.array_buffer : c->arrays.element_array_buffer); |
| |
| if (bo == 0) { |
| // can't modify buffer 0 |
| ogles_error(c, GL_INVALID_OPERATION); |
| return; |
| } |
| |
| buffer_t* edit_bo = const_cast<buffer_t*>(bo); |
| if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) { |
| ogles_error(c, GL_OUT_OF_MEMORY); |
| return; |
| } |
| if (data) { |
| memcpy(bo->data, data, size); |
| } |
| } |
| |
| void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| if (offset<0 || size<0 || data==0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? |
| c->arrays.array_buffer : c->arrays.element_array_buffer); |
| |
| if (bo == 0) { |
| // can't modify buffer 0 |
| ogles_error(c, GL_INVALID_OPERATION); |
| return; |
| } |
| if (offset+size > bo->size) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| memcpy(bo->data + offset, data, size); |
| } |
| |
| void glDeleteBuffers(GLsizei n, const GLuint* buffers) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (n<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| |
| for (int i=0 ; i<n ; i++) { |
| GLuint name = buffers[i]; |
| if (name) { |
| // unbind bound deleted buffers... |
| if (c->arrays.element_array_buffer->name == name) { |
| c->arrays.element_array_buffer = 0; |
| } |
| if (c->arrays.array_buffer->name == name) { |
| c->arrays.array_buffer = 0; |
| } |
| if (c->arrays.vertex.bo->name == name) { |
| c->arrays.vertex.bo = 0; |
| } |
| if (c->arrays.normal.bo->name == name) { |
| c->arrays.normal.bo = 0; |
| } |
| if (c->arrays.color.bo->name == name) { |
| c->arrays.color.bo = 0; |
| } |
| for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { |
| if (c->arrays.texture[t].bo->name == name) { |
| c->arrays.texture[t].bo = 0; |
| } |
| } |
| } |
| } |
| c->bufferObjectManager->deleteBuffers(n, buffers); |
| c->bufferObjectManager->recycleTokens(n, buffers); |
| } |
| |
| void glGenBuffers(GLsizei n, GLuint* buffers) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| if (n<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| c->bufferObjectManager->getToken(n, buffers); |
| } |