diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /opengl/libagl/primitives.cpp | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'opengl/libagl/primitives.cpp')
-rw-r--r-- | opengl/libagl/primitives.cpp | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp new file mode 100644 index 0000000..f164c02 --- /dev/null +++ b/opengl/libagl/primitives.cpp @@ -0,0 +1,1111 @@ +/* libs/opengles/primitives.cpp +** +** 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 <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include "context.h" +#include "primitives.h" +#include "light.h" +#include "matrix.h" +#include "vertex.h" +#include "fp.h" +#include "TextureObjectManager.h" + +extern "C" void iterators0032(const void* that, + int32_t* it, int32_t c0, int32_t c1, int32_t c2); + +namespace android { + +// ---------------------------------------------------------------------------- + +static void primitive_point(ogles_context_t* c, vertex_t* v); +static void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); +static void primitive_clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void primitive_nop_point(ogles_context_t* c, vertex_t* v); +static void primitive_nop_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); +static void primitive_nop_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static inline bool cull_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_texcoords(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_texcoords_w(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static unsigned int clip_line(ogles_context_t* c, + vertex_t* s, vertex_t* p); + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +static void lightTriangleDarkSmooth(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v0->flags & vertex_t::LIT)) { + v0->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v0->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v0->color.v, cp); + } + if (!(v1->flags & vertex_t::LIT)) { + v1->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v1->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v1->color.v, cp); + } + if(!(v2->flags & vertex_t::LIT)) { + v2->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v2->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v2->color.v, cp); + } +} + +static void lightTriangleDarkFlat(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v2->flags & vertex_t::LIT)) { + v2->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v2->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v2->color.v, cp); + } + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +static void lightTriangleSmooth(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v0->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v0); + if (!(v1->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v1); + if(!(v2->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v2); +} + +static void lightTriangleFlat(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v2->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +// The fog versions... + +static inline +void lightVertexDarkSmoothFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->flags |= vertex_t::LIT; + v->fog = c->fog.fog(c, v->eye.z); + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + } +} +static inline +void lightVertexDarkFlatFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->flags |= vertex_t::LIT; + v->fog = c->fog.fog(c, v->eye.z); + } +} +static inline +void lightVertexSmoothFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->fog = c->fog.fog(c, v->eye.z); + c->lighting.lightVertex(c, v); + } +} + +static void lightTriangleDarkSmoothFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkSmoothFog(c, v0); + lightVertexDarkSmoothFog(c, v1); + lightVertexDarkSmoothFog(c, v2); +} + +static void lightTriangleDarkFlatFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkFlatFog(c, v0); + lightVertexDarkFlatFog(c, v1); + lightVertexDarkSmoothFog(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +static void lightTriangleSmoothFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexSmoothFog(c, v0); + lightVertexSmoothFog(c, v1); + lightVertexSmoothFog(c, v2); +} + +static void lightTriangleFlatFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkFlatFog(c, v0); + lightVertexDarkFlatFog(c, v1); + lightVertexSmoothFog(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + + + +typedef void (*light_primitive_t)(ogles_context_t*, + vertex_t*, vertex_t*, vertex_t*); + +// fog 0x4, light 0x2, smooth 0x1 +static const light_primitive_t lightPrimitive[8] = { + lightTriangleDarkFlat, // no fog | dark | flat + lightTriangleDarkSmooth, // no fog | dark | smooth + lightTriangleFlat, // no fog | light | flat + lightTriangleSmooth, // no fog | light | smooth + lightTriangleDarkFlatFog, // fog | dark | flat + lightTriangleDarkSmoothFog, // fog | dark | smooth + lightTriangleFlatFog, // fog | light | flat + lightTriangleSmoothFog // fog | light | smooth +}; + +void ogles_validate_primitives(ogles_context_t* c) +{ + const uint32_t enables = c->rasterizer.state.enables; + + // set up the lighting/shading/smoothing/fogging function + int index = enables & GGL_ENABLE_SMOOTH ? 0x1 : 0; + index |= c->lighting.enable ? 0x2 : 0; + index |= enables & GGL_ENABLE_FOG ? 0x4 : 0; + c->lighting.lightTriangle = lightPrimitive[index]; + + // set up the primitive renderers + if (ggl_likely(c->arrays.vertex.enable)) { + c->prims.renderPoint = primitive_point; + c->prims.renderLine = primitive_line; + c->prims.renderTriangle = primitive_clip_triangle; + } else { + c->prims.renderPoint = primitive_nop_point; + c->prims.renderLine = primitive_nop_line; + c->prims.renderTriangle = primitive_nop_triangle; + } +} + +// ---------------------------------------------------------------------------- + +void compute_iterators_t::initTriangle( + vertex_t const* v0, vertex_t const* v1, vertex_t const* v2) +{ + m_dx01 = v1->window.x - v0->window.x; + m_dy10 = v0->window.y - v1->window.y; + m_dx20 = v0->window.x - v2->window.x; + m_dy02 = v2->window.y - v0->window.y; + m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; +} + +void compute_iterators_t::initLine( + vertex_t const* v0, vertex_t const* v1) +{ + m_dx01 = m_dy02 = v1->window.x - v0->window.x; + m_dy10 = m_dx20 = v0->window.y - v1->window.y; + m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; +} + +void compute_iterators_t::initLerp(vertex_t const* v0, uint32_t enables) +{ + m_x0 = v0->window.x; + m_y0 = v0->window.y; + const GGLcoord area = (m_area + TRI_HALF) >> TRI_FRACTION_BITS; + const GGLcoord minArea = 2; // cannot be inverted + // triangles with an area smaller than 1.0 are not smooth-shaded + + int q=0, s=0, d=0; + if (abs(area) >= minArea) { + // Here we do some voodoo magic, to compute a suitable scale + // factor for deltas/area: + + // First compute the 1/area with full 32-bits precision, + // gglRecipQNormalized returns a number [-0.5, 0.5[ and an exponent. + d = gglRecipQNormalized(area, &q); + + // Then compute the minimum left-shift to not overflow the muls + // below. + s = 32 - gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); + + // We'll keep 16-bits of precision for deltas/area. So we need + // to shift everything left an extra 15 bits. + s += 15; + + // make sure all final shifts are not > 32, because gglMulx + // can't handle it. + if (s < q) s = q; + if (s > 32) { + d >>= 32-s; + s = 32; + } + } + + m_dx01 = gglMulx(m_dx01, d, s); + m_dy10 = gglMulx(m_dy10, d, s); + m_dx20 = gglMulx(m_dx20, d, s); + m_dy02 = gglMulx(m_dy02, d, s); + m_area_scale = 32 + q - s; + m_scale = 0; + + if (enables & GGL_ENABLE_TMUS) { + const int A = gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); + const int B = gglClz(abs(m_x0)|abs(m_y0)); + m_scale = max(0, 32 - (A + 16)) + + max(0, 32 - (B + TRI_FRACTION_BITS)) + 1; + } +} + +int compute_iterators_t::iteratorsScale(GGLfixed* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + int32_t dc01 = c1 - c0; + int32_t dc02 = c2 - c0; + const int A = gglClz(abs(c0)); + const int B = gglClz(abs(dc01)|abs(dc02)); + const int scale = min(A, B - m_scale) - 2; + if (scale >= 0) { + c0 <<= scale; + dc01 <<= scale; + dc02 <<= scale; + } else { + c0 >>= -scale; + dc01 >>= -scale; + dc02 >>= -scale; + } + const int s = m_area_scale; + int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); + int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); + int32_t c = c0 - (gglMulAddx(dcdx, m_x0, + gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); + it[0] = c; + it[1] = dcdx; + it[2] = dcdy; + return scale; +} + +void compute_iterators_t::iterators1616(GGLfixed* it, + GGLfixed c0, GGLfixed c1, GGLfixed c2) const +{ + const GGLfixed dc01 = c1 - c0; + const GGLfixed dc02 = c2 - c0; + // 16.16 x 16.16 == 32.32 --> 16.16 + const int s = m_area_scale; + int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); + int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); + int32_t c = c0 - (gglMulAddx(dcdx, m_x0, + gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); + it[0] = c; + it[1] = dcdx; + it[2] = dcdy; +} + +void compute_iterators_t::iterators0032(int64_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + const int s = m_area_scale - 16; + int32_t dc01 = (c1 - c0)>>s; + int32_t dc02 = (c2 - c0)>>s; + // 16.16 x 16.16 == 32.32 + int64_t dcdx = gglMulii(dc01, m_dy02) + gglMulii(dc02, m_dy10); + int64_t dcdy = gglMulii(dc02, m_dx01) + gglMulii(dc01, m_dx20); + it[ 0] = (c0<<16) - ((dcdx*m_x0 + dcdy*m_y0)>>4); + it[ 1] = dcdx; + it[ 2] = dcdy; +} + +#if defined(__arm__) && !defined(__thumb__) +inline void compute_iterators_t::iterators0032(int32_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + ::iterators0032(this, it, c0, c1, c2); +} +#else +void compute_iterators_t::iterators0032(int32_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + int64_t it64[3]; + iterators0032(it, c0, c1, c2); + it[0] = it64[0]; + it[1] = it64[1]; + it[2] = it64[2]; +} +#endif + +// ---------------------------------------------------------------------------- + +static inline int32_t clampZ(GLfixed z) CONST; +int32_t clampZ(GLfixed z) { + z = (z & ~(z>>31)); + if (z >= 0x10000) + z = 0xFFFF; + return z; +} + +static __attribute__((noinline)) +void fetch_texcoord_impl(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + vertex_t* const vtx[3] = { v0, v1, v2 }; + array_t const * const texcoordArray = c->arrays.texture; + + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (!(c->rasterizer.state.texture[i].enable)) + continue; + + for (int j=0 ; j<3 ; j++) { + vertex_t* const v = vtx[j]; + if (v->flags & vertex_t::TT) + continue; + + // NOTE: here we could compute automatic texgen + // such as sphere/cube maps, instead of fetching them + // from the textcoord array. + + vec4_t& coords = v->texture[i]; + const GLubyte* tp = texcoordArray[i].element( + v->index & vertex_cache_t::INDEX_MASK); + texcoordArray[i].fetch(c, coords.v, tp); + + // transform texture coordinates... + coords.Q = 0x10000; + const transform_t& tr = c->transforms.texture[i].transform; + if (ggl_unlikely(tr.ops)) { + c->arrays.tex_transform[i](&tr, &coords, &coords); + } + + // divide by Q + const GGLfixed q = coords.Q; + if (ggl_unlikely(q != 0x10000)) { + const int32_t qinv = gglRecip28(q); + coords.S = gglMulx(coords.S, qinv, 28); + coords.T = gglMulx(coords.T, qinv, 28); + } + } + } + v0->flags |= vertex_t::TT; + v1->flags |= vertex_t::TT; + v2->flags |= vertex_t::TT; +} + +inline void fetch_texcoord(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const uint32_t enables = c->rasterizer.state.enables; + if (!(enables & GGL_ENABLE_TMUS)) + return; + + // Fetch & transform texture coordinates... + if (ggl_likely(v0->flags & v1->flags & v2->flags & vertex_t::TT)) { + // already done for all three vertices, bail... + return; + } + fetch_texcoord_impl(c, v0, v1, v2); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Point +#endif + +void primitive_nop_point(ogles_context_t*, vertex_t*) { +} + +void primitive_point(ogles_context_t* c, vertex_t* v) +{ + // lighting & clamping... + const uint32_t enables = c->rasterizer.state.enables; + + if (ggl_unlikely(!(v->flags & vertex_t::LIT))) { + if (c->lighting.enable) { + c->lighting.lightVertex(c, v); + } else { + v->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + } + if (enables & GGL_ENABLE_FOG) { + v->fog = c->fog.fog(c, v->eye.z); + } + } + + // XXX: we don't need to do that each-time + // if color array and lighting not enabled + c->rasterizer.procs.color4xv(c, v->color.v); + + // XXX: look into ES point-sprite extension + if (enables & GGL_ENABLE_TMUS) { + fetch_texcoord(c, v,v,v); + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (!c->rasterizer.state.texture[i].enable) + continue; + int32_t itt[8]; + itt[1] = itt[2] = itt[4] = itt[5] = 0; + itt[6] = itt[7] = 16; // XXX: check that + if (c->rasterizer.state.texture[i].s_wrap == GGL_CLAMP) { + int width = c->textures.tmu[i].texture->surface.width; + itt[0] = v->texture[i].S * width; + itt[6] = 0; + } + if (c->rasterizer.state.texture[i].t_wrap == GGL_CLAMP) { + int height = c->textures.tmu[i].texture->surface.height; + itt[3] = v->texture[i].T * height; + itt[7] = 0; + } + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + int32_t itz[3]; + itz[0] = clampZ(v->window.z) * 0x00010001; + itz[1] = itz[2] = 0; + c->rasterizer.procs.zGrad3xv(c, itz); + } + + if (enables & GGL_ENABLE_FOG) { + GLfixed itf[3]; + itf[0] = v->fog; + itf[1] = itf[2] = 0; + c->rasterizer.procs.fogGrad3xv(c, itf); + } + + // Render our point... + c->rasterizer.procs.pointx(c, v->window.v, c->point.size); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Line +#endif + +void primitive_nop_line(ogles_context_t*, vertex_t*, vertex_t*) { +} + +void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1) +{ + // get texture coordinates + fetch_texcoord(c, v0, v1, v1); + + // light/shade the vertices first (they're copied below) + c->lighting.lightTriangle(c, v0, v1, v1); + + // clip the line if needed + if (ggl_unlikely((v0->flags | v1->flags) & vertex_t::CLIP_ALL)) { + unsigned int count = clip_line(c, v0, v1); + if (ggl_unlikely(count == 0)) + return; + } + + // compute iterators... + const uint32_t enables = c->rasterizer.state.enables; + const uint32_t mask = GGL_ENABLE_TMUS | + GGL_ENABLE_SMOOTH | + GGL_ENABLE_W | + GGL_ENABLE_FOG | + GGL_ENABLE_DEPTH_TEST; + + if (ggl_unlikely(enables & mask)) { + c->lerp.initLine(v0, v1); + lerp_triangle(c, v0, v1, v0); + } + + // render our line + c->rasterizer.procs.linex(c, v0->window.v, v1->window.v, c->line.width); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Triangle +#endif + +void primitive_nop_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) { +} + +void primitive_clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + uint32_t cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; + if (ggl_likely(!cc)) { + // code below must be as optimized as possible, this is the + // common code path. + + // This triangle is not clipped, test if it's culled + // unclipped triangle... + c->lerp.initTriangle(v0, v1, v2); + if (cull_triangle(c, v0, v1, v2)) + return; // culled! + + // Fetch all texture coordinates if needed + fetch_texcoord(c, v0, v1, v2); + + // light (or shade) our triangle! + c->lighting.lightTriangle(c, v0, v1, v2); + + triangle(c, v0, v1, v2); + return; + } + + // The assumption here is that we're not going to clip very often, + // and even more rarely will we clip a triangle that ends up + // being culled out. So it's okay to light the vertices here, even though + // in a few cases we won't render the triangle (if culled). + + // Fetch texture coordinates... + fetch_texcoord(c, v0, v1, v2); + + // light (or shade) our triangle! + c->lighting.lightTriangle(c, v0, v1, v2); + + clip_triangle(c, v0, v1, v2); +} + +// ----------------------------------------------------------------------- + +void triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + // compute iterators... + const uint32_t enables = c->rasterizer.state.enables; + const uint32_t mask = GGL_ENABLE_TMUS | + GGL_ENABLE_SMOOTH | + GGL_ENABLE_W | + GGL_ENABLE_FOG | + GGL_ENABLE_DEPTH_TEST; + + if (ggl_likely(enables & mask)) + lerp_triangle(c, v0, v1, v2); + + c->rasterizer.procs.trianglex(c, v0->window.v, v1->window.v, v2->window.v); +} + +void lerp_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const uint32_t enables = c->rasterizer.state.enables; + c->lerp.initLerp(v0, enables); + + // set up texture iterators + if (enables & GGL_ENABLE_TMUS) { + if (enables & GGL_ENABLE_W) { + lerp_texcoords_w(c, v0, v1, v2); + } else { + lerp_texcoords(c, v0, v1, v2); + } + } + + // set up the color iterators + const compute_iterators_t& lerp = c->lerp; + if (enables & GGL_ENABLE_SMOOTH) { + GLfixed itc[12]; + for (int i=0 ; i<4 ; i++) { + const GGLcolor c0 = v0->color.v[i] * 255; + const GGLcolor c1 = v1->color.v[i] * 255; + const GGLcolor c2 = v2->color.v[i] * 255; + lerp.iterators1616(&itc[i*3], c0, c1, c2); + } + c->rasterizer.procs.colorGrad12xv(c, itc); + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + int32_t itz[3]; + const int32_t v0z = clampZ(v0->window.z); + const int32_t v1z = clampZ(v1->window.z); + const int32_t v2z = clampZ(v2->window.z); + if (ggl_unlikely(c->polygonOffset.enable)) { + const int32_t units = (c->polygonOffset.units << 16); + const GLfixed factor = c->polygonOffset.factor; + if (factor) { + int64_t itz64[3]; + lerp.iterators0032(itz64, v0z, v1z, v2z); + int64_t maxDepthSlope = max(itz64[1], itz64[2]); + itz[0] = uint32_t(itz64[0]) + + uint32_t((maxDepthSlope*factor)>>16) + units; + itz[1] = uint32_t(itz64[1]); + itz[2] = uint32_t(itz64[2]); + } else { + lerp.iterators0032(itz, v0z, v1z, v2z); + itz[0] += units; + } + } else { + lerp.iterators0032(itz, v0z, v1z, v2z); + } + c->rasterizer.procs.zGrad3xv(c, itz); + } + + if (ggl_unlikely(enables & GGL_ENABLE_FOG)) { + GLfixed itf[3]; + lerp.iterators1616(itf, v0->fog, v1->fog, v2->fog); + c->rasterizer.procs.fogGrad3xv(c, itf); + } +} + + +static inline +int compute_lod(ogles_context_t* c, int i, + int32_t s0, int32_t t0, int32_t s1, int32_t t1, int32_t s2, int32_t t2) +{ + // Compute mipmap level / primitive + // rho = sqrt( texelArea / area ) + // lod = log2( rho ) + // lod = log2( texelArea / area ) / 2 + // lod = (log2( texelArea ) - log2( area )) / 2 + const compute_iterators_t& lerp = c->lerp; + const GGLcoord area = abs(lerp.area()); + const int w = c->textures.tmu[i].texture->surface.width; + const int h = c->textures.tmu[i].texture->surface.height; + const int shift = 16 + (16 - TRI_FRACTION_BITS); + int32_t texelArea = abs( gglMulx(s1-s0, t2-t0, shift) - + gglMulx(s2-s0, t1-t0, shift) )*w*h; + int log2TArea = (32-TRI_FRACTION_BITS -1) - gglClz(texelArea); + int log2Area = (32-TRI_FRACTION_BITS*2-1) - gglClz(area); + int lod = (log2TArea - log2Area + 1) >> 1; + return lod; +} + +void lerp_texcoords(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const compute_iterators_t& lerp = c->lerp; + int32_t itt[8] __attribute__((aligned(16))); + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + const texture_t& tmu = c->rasterizer.state.texture[i]; + if (!tmu.enable) + continue; + + // compute the jacobians using block floating-point + int32_t s0 = v0->texture[i].S; + int32_t t0 = v0->texture[i].T; + int32_t s1 = v1->texture[i].S; + int32_t t1 = v1->texture[i].T; + int32_t s2 = v2->texture[i].S; + int32_t t2 = v2->texture[i].T; + + const GLenum min_filter = c->textures.tmu[i].texture->min_filter; + if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { + int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); + c->rasterizer.procs.bindTextureLod(c, i, + &c->textures.tmu[i].texture->mip(lod)); + } + + // premultiply (s,t) when clampling + if (tmu.s_wrap == GGL_CLAMP) { + const int width = tmu.surface.width; + s0 *= width; + s1 *= width; + s2 *= width; + } + if (tmu.t_wrap == GGL_CLAMP) { + const int height = tmu.surface.height; + t0 *= height; + t1 *= height; + t2 *= height; + } + itt[6] = -lerp.iteratorsScale(itt+0, s0, s1, s2); + itt[7] = -lerp.iteratorsScale(itt+3, t0, t1, t2); + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } +} + +void lerp_texcoords_w(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const compute_iterators_t& lerp = c->lerp; + int32_t itt[8] __attribute__((aligned(16))); + int32_t itw[3]; + + // compute W's scale to 2.30 + int32_t w0 = v0->window.w; + int32_t w1 = v1->window.w; + int32_t w2 = v2->window.w; + int wscale = 32 - gglClz(w0|w1|w2); + + // compute the jacobian using block floating-point + int sc = lerp.iteratorsScale(itw, w0, w1, w2); + sc += wscale - 16; + c->rasterizer.procs.wGrad3xv(c, itw); + + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + const texture_t& tmu = c->rasterizer.state.texture[i]; + if (!tmu.enable) + continue; + + // compute the jacobians using block floating-point + int32_t s0 = v0->texture[i].S; + int32_t t0 = v0->texture[i].T; + int32_t s1 = v1->texture[i].S; + int32_t t1 = v1->texture[i].T; + int32_t s2 = v2->texture[i].S; + int32_t t2 = v2->texture[i].T; + + const GLenum min_filter = c->textures.tmu[i].texture->min_filter; + if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { + int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); + c->rasterizer.procs.bindTextureLod(c, i, + &c->textures.tmu[i].texture->mip(lod)); + } + + // premultiply (s,t) when clampling + if (tmu.s_wrap == GGL_CLAMP) { + const int width = tmu.surface.width; + s0 *= width; + s1 *= width; + s2 *= width; + } + if (tmu.t_wrap == GGL_CLAMP) { + const int height = tmu.surface.height; + t0 *= height; + t1 *= height; + t2 *= height; + } + + s0 = gglMulx(s0, w0, wscale); + t0 = gglMulx(t0, w0, wscale); + s1 = gglMulx(s1, w1, wscale); + t1 = gglMulx(t1, w1, wscale); + s2 = gglMulx(s2, w2, wscale); + t2 = gglMulx(t2, w2, wscale); + + itt[6] = sc - lerp.iteratorsScale(itt+0, s0, s1, s2); + itt[7] = sc - lerp.iteratorsScale(itt+3, t0, t1, t2); + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } +} + + +static inline +bool cull_triangle(ogles_context_t* c, vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (ggl_likely(c->cull.enable)) { + const GLenum winding = (c->lerp.area() > 0) ? GL_CW : GL_CCW; + const GLenum face = (winding == c->cull.frontFace) ? GL_FRONT : GL_BACK; + if (face == c->cull.cullFace) + return true; // culled! + } + return false; +} + +static inline +GLfixed frustumPlaneDist(int plane, const vec4_t& s) +{ + const GLfixed d = s.v[ plane >> 1 ]; + return ((plane & 1) ? (s.w - d) : (s.w + d)); +} + +static inline +int32_t clipDivide(GLfixed a, GLfixed b) { + // returns a 4.28 fixed-point + return gglMulDivi(1LU<<28, a, b); +} + +void clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + uint32_t all_cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; + + vertex_t *p0, *p1, *p2; + const int MAX_CLIPPING_PLANES = 6 + OGLES_MAX_CLIP_PLANES; + const int MAX_VERTICES = 3; + + // Temporary buffer to hold the new vertices. Each plane can add up to + // two new vertices (because the polygon is convex). + // We need one extra element, to handle an overflow case when + // the polygon degenerates into something non convex. + vertex_t buffer[MAX_CLIPPING_PLANES * 2 + 1]; // ~3KB + vertex_t* buf = buffer; + + // original list of vertices (polygon to clip, in fact this + // function works with an arbitrary polygon). + vertex_t* in[3] = { v0, v1, v2 }; + + // output lists (we need 2, which we use back and forth) + // (maximum outpout list's size is MAX_CLIPPING_PLANES + MAX_VERTICES) + // 2 more elements for overflow when non convex polygons. + vertex_t* out[2][MAX_CLIPPING_PLANES + MAX_VERTICES + 2]; + unsigned int outi = 0; + + // current input list + vertex_t** ivl = in; + + // 3 input vertices, 0 in the output list, first plane + unsigned int ic = 3; + + // User clip-planes first, the clipping is always done in eye-coordinate + // this is basically the same algorithm than for the view-volume + // clipping, except for the computation of the distance (vertex, plane) + // and the fact that we need to compute the eye-coordinates of each + // new vertex we create. + + if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) + { + unsigned int plane = 0; + uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; + do { + if (cc & 1) { + // pointers to our output list (head and current) + vertex_t** const ovl = &out[outi][0]; + vertex_t** output = ovl; + unsigned int oc = 0; + unsigned int sentinel = 0; + // previous vertex, compute distance to the plane + vertex_t* s = ivl[ic-1]; + const vec4_t& equation = c->clipPlanes.plane[plane].equation; + GLfixed sd = dot4(equation.v, s->eye.v); + // clip each vertex against this plane... + for (unsigned int i=0 ; i<ic ; i++) { + vertex_t* p = ivl[i]; + const GLfixed pd = dot4(equation.v, p->eye.v); + if (sd >= 0) { + if (pd >= 0) { + // both inside + *output++ = p; + oc++; + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipEye(c, buf, t, p, s); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipEye(c, buf, t, s, p); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + *output++ = p; + oc++; + } else { + // both outside + } + } + s = p; + sd = pd; + } + // output list become the new input list + if (oc<3) + return; // less than 3 vertices left? we're done! + ivl = ovl; + ic = oc; + outi = 1-outi; + } + cc >>= 1; + plane++; + } while (cc); + } + + // frustum clip-planes + if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) + { + unsigned int plane = 0; + uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; + do { + if (cc & 1) { + // pointers to our output list (head and current) + vertex_t** const ovl = &out[outi][0]; + vertex_t** output = ovl; + unsigned int oc = 0; + unsigned int sentinel = 0; + // previous vertex, compute distance to the plane + vertex_t* s = ivl[ic-1]; + GLfixed sd = frustumPlaneDist(plane, s->clip); + // clip each vertex against this plane... + for (unsigned int i=0 ; i<ic ; i++) { + vertex_t* p = ivl[i]; + const GLfixed pd = frustumPlaneDist(plane, p->clip); + if (sd >= 0) { + if (pd >= 0) { + // both inside + *output++ = p; + oc++; + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipVertex(c, buf, t, p, s); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipVertex(c, buf, t, s, p); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + *output++ = p; + oc++; + } else { + // both outside + } + } + s = p; + sd = pd; + } + // output list become the new input list + if (oc<3) + return; // less than 3 vertices left? we're done! + ivl = ovl; + ic = oc; + outi = 1-outi; + } + cc >>= 1; + plane++; + } while (cc); + } + + // finally we can render our triangles... + p0 = ivl[0]; + p1 = ivl[1]; + for (unsigned int i=2 ; i<ic ; i++) { + p2 = ivl[i]; + c->lerp.initTriangle(p0, p1, p2); + if (cull_triangle(c, p0, p1, p2)) { + p1 = p2; + continue; // culled! + } + triangle(c, p0, p1, p2); + p1 = p2; + } +} + +unsigned int clip_line(ogles_context_t* c, vertex_t* s, vertex_t* p) +{ + const uint32_t all_cc = (s->flags | p->flags) & vertex_t::CLIP_ALL; + + if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) + { + unsigned int plane = 0; + uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; + do { + if (cc & 1) { + const vec4_t& equation = c->clipPlanes.plane[plane].equation; + const GLfixed sd = dot4(equation.v, s->eye.v); + const GLfixed pd = dot4(equation.v, p->eye.v); + if (sd >= 0) { + if (pd >= 0) { + // both inside + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipEye(c, p, t, p, s); + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipEye(c, s, t, s, p); + } + } else { + // both outside + return 0; + } + } + } + cc >>= 1; + plane++; + } while (cc); + } + + // frustum clip-planes + if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) + { + unsigned int plane = 0; + uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; + do { + if (cc & 1) { + const GLfixed sd = frustumPlaneDist(plane, s->clip); + const GLfixed pd = frustumPlaneDist(plane, p->clip); + if (sd >= 0) { + if (pd >= 0) { + // both inside + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipVertex(c, p, t, p, s); + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipVertex(c, s, t, s, p); + } + } else { + // both outside + return 0; + } + } + } + cc >>= 1; + plane++; + } while (cc); + } + + return 2; +} + + +}; // namespace android |