69ca17a124
This bug was introduced when lighting computations was changed from eye-space to object-space. The light position need to be transformed back to object-space each time the modelview matrix changes which requires us to compute the inverse of the modelview matrix. This computation was done with the assumption that normals where transformed (which was the case when the computation was made in eye-space), however, normals only require the inverse of the upper 3x3 matrix while transforming positions requires the inverse of the whole matrix. This caused the interesting behavior that lights were more-or-less transformed properly, but not translated at all, which caused improper lighting with directional lights in particular. There was also another smaller bug affecting directional lights: when vertices are read, only the active component are read, the other ones are ignored, later, the transformation operations are set up to ignore the unset values, howver, in the case of lighting, we use the vertex in object space (that is, before it is transformed), and therefore were using uninitalized values; in particular w.
1562 lines
46 KiB
C++
1562 lines
46 KiB
C++
/*
|
|
** 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);
|
|
}
|